INeedHelp
INeedHelp

Reputation: 101

ASCII Art - Sorting an array of ASCII characters by brightness levels (C/C++)

I am working on a program (in C/C++) where the user should take a user selected image and convert this into ASCII art (using SQL). So far I have been able for the user to select an image and display this image in greyscale in a window.

However, I am currently having difficulties sorting the ASCII characters as an array of brightness levels and then matching the single character to a pixel area.

I am aware that I will need to use 'for' loops, but I can't find anything that useful on the internet so far. Thank you :D

Upvotes: 9

Views: 27522

Answers (4)

Ahsen Khan
Ahsen Khan

Reputation: 391

$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`'.

Here's a standard grayscale from dark to light (or the other way around depending on your preferred display colour.)

To save your time, this particular gradient contains 70 characters including the space.

Upvotes: 27

chungaloider
chungaloider

Reputation: 141

 `.-':_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&@

I found Ahsen Khan's answer not to be exactly accurate for my Windows console's font (consolas), which produced slightly off results. I wrote a program to analyse and order the characters by summing the greyscale values of each pixel, which produced this. Don't forget to include the space at the beginning.

The characters also aren't distributed evenly by darkness (although I found the results of assuming an even distribution to be fine). The darkness for each character is:

[0, 0.0751, 0.0829, 0.0848, 0.1227, 0.1403, 0.1559, 0.185, 0.2183, 0.2417, 0.2571, 0.2852, 0.2902, 0.2919, 0.3099, 0.3192, 0.3232, 0.3294, 0.3384, 0.3609, 0.3619, 0.3667, 0.3737, 0.3747, 0.3838, 0.3921, 0.396, 0.3984, 0.3993, 0.4075, 0.4091, 0.4101, 0.42, 0.423, 0.4247, 0.4274, 0.4293, 0.4328, 0.4382, 0.4385, 0.442, 0.4473, 0.4477, 0.4503, 0.4562, 0.458, 0.461, 0.4638, 0.4667, 0.4686, 0.4693, 0.4703, 0.4833, 0.4881, 0.4944, 0.4953, 0.4992, 0.5509, 0.5567, 0.5569, 0.5591, 0.5602, 0.5602, 0.565, 0.5776, 0.5777, 0.5818, 0.587, 0.5972, 0.5999, 0.6043, 0.6049, 0.6093, 0.6099, 0.6465, 0.6561, 0.6595, 0.6631, 0.6714, 0.6759, 0.6809, 0.6816, 0.6925, 0.7039, 0.7086, 0.7235, 0.7302, 0.7332, 0.7602, 0.7834, 0.8037, 0.9999]

These values are scaled for 0 being all black pixels (i.e. the space character), and 0.9999 being the brightest ('@').

Upvotes: 13

Jongware
Jongware

Reputation: 22478

ASCII characters have no intrinsic grayness, because this only depends on how they are drawn on your screen - in what font, size, and horizontal and vertical spacing, and in what colors.

Still: it's possible to make an educated guess. A space is 'all white' for most fonts, a full stop will be a small dot in most fonts, a comma is slightly larger, et cetera. All you need is the ratio of black-to-white pixels, for each displayable character.

  1. Find (or create) a bitmapped version of the font you are going to use, or one close to it. Since most ASCII fonts are actually very close in their design, you can even use a simple 8x8 bitmap font (not many fonts will have a ! that is "heavier" than a W - if you find one that does, find another). As you are going to use it for ASCII art, you probably want monospaced fonts only. Drawing ASCII art with proportional fonts is possible but much harder.

  2. Loop over each character and count the black pixels. Typically, a space will have 0, a full stop 1, and something like a # will have 16 or so. (Hey look! This is where you can use a for loop!)

  3. The exact grayness of each character can be represented by value/area where area is the entire size of the bitmap. But you are not interested in the absolute value! A normal ASCII range does not contain a character that should be "all black". To find out the relative grayness, divide the value for each character by the maximum value you found for the entire font. That way, the space will be 0, the "maximum value" character will be 1, and all other characters will have a value in between. (Actually, if your Space character contains black pixels, subtract these from the initial values as well. Just to outline the general idea; you'd better not use such a weird font.)

  4. You will end up with 95 values - one for each from Space (#32) to Tilde (#126). If you want to use all characters in your output, you are ready to go. Sort the characters on their gray level. For each gray input pixel (a level from 0 to 255), divide it by 255 to get it into the same range as your character map value. Then print the character with a level closest to it. (This is the most straightforward method but can be optimized in various ways.)

  5. If you want to restrict the grayscale to a smaller range of values/characters, build a smaller array with values closest to the levels you want. For example, if you want 16 levels of gray, find the characters closest to 0, 1/15, 2/15 ... 15/15.

See also Step 5 and 6 of my answer to a related question.

Upvotes: 2

MSalters
MSalters

Reputation: 180145

There's no reasonable way to do this, and existing ASCII-art programs just use a predefined character order. I.e. char myASCII[] = " .,:ilwW";

Upvotes: 3

Related Questions