I've seen plenty of image to ascii art converters, but what I need is a little more complex. Instead of a mosaic image created from ascii chars I need to be able to use a predefined set of strings of arbitrary lengths as my mosaic tiles.
I've done a good bit of Googling, but Im not even sure how to structure my query? Does anything like this exist? And bonus if it wont use duplicates in its image generation.
Not really suitable for Stack Overflow, but an interesting project nevertheless. So I had a go at it for a bit of fun and see how far I could get.
I think it comes down to this:
I don't think "without duplicates" is feasible, unless you have very small images and lots of candidate strings. This is what I came up with, using my avatar and my current badges as list of strings. (I excluded my "c" badge; somehow my program decided that was the 'best fit' for large patches, which wasn't very attractive. Mental note: Do Not Include 1-Character Strings.)
You have to squint a bit; at a small size it looks like this:
Here is how I created it.
Step 1: Find a suitable image;
Step 2: convert to grayscale;
Step 3: convert gray to extremes. This step is to ensure the input range (gray values) uses the full range of 0 to 255.
Step 4: resize the image for a best fit. I chose 80x40, and deliberately squashed the image by half. This because 'text' usually is higher than it is wide. Different fonts need different aspect ratios! The
80
is going to be the number of characters per line, the40
is total number of lines.I used Photoshop for the above steps, just because I did not want to write code for it. It's not hard or something, as long as you have access to the raw image data, but it's a lot of work and not interesting.
Intermediate step, enlarged by 400% so you can see the pixels:
Step 5: Find yourself a monospaced bitmap font. I found a nice 8x8 one somewhere on the 'web. Perhaps a larger size may work better, but only very minor, because the limiting factor is your strings, not the font.
Step 6: Calculate 'gray' values for each of the ASCII characters. This is the number of black pixels, divided by the total number of pixels. For a better spread, I divided the result for each character by the maximum found, so the lowest value is
0
(for space) and the highest is1
(which happened to be forM
, but it depends on the font). I then multiplied the values by 255, so it mimics grayscale values. Finally, as these values were the reverse of those in a grayscale image, I replaced them with255-value
.In between I did a lot of testing to make sure my initial idea was still sound. Here is a dump of my test image using a plain gray-to-character translation:
This is similar to what your average image to ASCII art converter outputs.
Step 7: find yourself a list of strings to use.
Step 8: starting at the top left, test the coverage of each of your strings against the image. Print out the best fit, increase the position by this strings' length, repeat until done. (If you want no duplicates, you remove the string from your pool at this point.)
Step 9: profit!
For the coverage test, I used the sum of
(source - dest)²
for each character/pixel, divided by the length of the string: lower = better -- the smallest difference between the string and the destination.I did not consider pretty line endings here. I experimented with giving a negative bonus if a word filled a line exactly, but the difference in output was minor. It may still work with a larger set of strings.
A possible improvement is to test a sequence of strings, i.e., instead of the greedy approach here, use a dynamic programming approach, much like Donald Knuth devised to decide the best breaks in word wrapping text.