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:
- calculate a gray value for a set of ASCII characters;
- calculate the 'best fit' for each string;
- repeat 2. until done.
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.)
QuorumQuorumAutobiographerQuorumQuorumQuorumJongwareQuorumQuorumQuorumQuorumQuor
umQuorumQuorumAutobiographerQuorumQuorumSupporterQuorumQuorumProofreaderQuorumQu
orumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuorum
QuorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuor
umQuorumQuorumAutobiographerQuorumQuorumQuorumAutobiographerQuorumMortarboardQuo
rumQuorumAutobiographerQuorumQuorumQuorumQuorumAutobiographerQuorumMortarboardQu
orumQuorumAutobiographerQuorumQuorumQuorumQuorumJongwareQuorumQuorumCommentatorQ
uorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuorumQuorumQuorumAutobiographe
rQuorumQuorumAutobiographerQuorumQuorumProofreaderJongwareQuorumQuorumQuorumQuor
umQuorumQuorumMortarboardJongwareQuorumProofreaderCommentatorSuffrageQuorumQuoru
mQuorumQuorumJongwareQuorumAutobiographerSuffrageCommentatorCaucusCriticCleanupQ
uorumQuorumMortarboardAutobiographerCommentatorQuorumConstituentCriticCriticQuor
umQuorumQuorumQuorumAutobiographerJongwareCleanupSupporterInvestorCriticCleanupQ
uorumQuorumQuorumQuorumAutobiographerFanaticCleanupFanaticInvestorCriticCleanupQ
uorumQuorumQuorumQuorumAutobiographerCriticInformedSupporterCriticCriticInformed
QuorumQuorumQuorumQuorumAutobiographerCriticQuorumSupporterInvestorFanaticQuorum
QuorumQuorumQuorumQuorumAutobiographerCriticCleanupCommentatorQuorumDeputyQuorum
QuorumQuorumQuorumQuorumAutobiographerCriticInvestorCaucusInformedDeputyQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorCriticCriticCitizen PatrolQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumAutobiographerCriticCriticCitizen PatrolQuorumQuorumQ
uorumQuorumQuorumQuorumQuorumAutobiographerCriticCriticCitizen PatrolStewardQuor
umQuorumQuorumQuorumQuorumAutobiographerConstituentCitizen PatrolCaucusQuorumQuo
rumQuorumQuorumQuorumQuorumAutobiographerInvestorCriticConstituentQuorumQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorConstituentCleanupCaucusCleanupQuorumQuor
umQuorumQuorumQuorumQuorumAutobiographerInvestorCleanupSupporterInformedQuorumQu
orumQuorumQuorumAutobiographerCommentatorCriticInformedJongwareJongwareQuorumQuo
rumQuorumQuorumAutobiographerCommentatorCriticInvestorJongwareJongwareQuorumQuor
umQuorumQuorumAutobiographerFanaticInvestorCriticInformedCleanupQuorumQuorumQuor
umQuorumQuorumQuorumDeputySupporterInvestorConstituentCaucusQuorumQuorumQuorumQu
orumQuorumQuorumAutobiographerCriticInvestorCriticSupporterQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumAutobiographerInvestorInvestorCleanupQuorumQuorumQuorumQuor
umQuorumQuorumQuorumQuorumCommentatorInvestorInvestorQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumCommentatorInvestorInvestorQuorumQuorumQuorumQuorumQu
orumQuorumQuorumQuorumQuorumCommentatorInvestorCleanupStewardQuorumQuorumQuorumQ
uorumQuorumQuorumQuorumQuorumCommentatorInvestorJongwareQuorumQuorumQuorumQuorum
QuorumQuorumQuorumQuorumQuorumAutobiographerCleanupQuorumQuorumQuorumQuorumQuoru
mQuorumQuorumQuorumQuorumQuorumAutobiographerSuffrageQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumAutobiographerDeputyQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumQuorumAutobiographerQuorumQuorumQuorumQuorumQuo
rumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuorumQuoru
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, the 40
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 is 1
(which happened to be for M
, 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 with 255-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:
0MMMMMM00000000000RDRRDDDDDDDDDDDDDDR#@RRRRR#00000RRR@RRRRR@RR@RRRRD&44�#0000
MMMMMM000000000000RDRDDDDDDDDDDDDDDDR0####0R&&&&&&DR@RRR@#########RRDDD4@0000000
MMM000#00000000000RDRRDDDDDDDDDDD&&&R00#000@&DR@####RR@#0000000##0#RD4PP&R@00000
M000#0000000000000RDDDDDDDDDDD&D&&D&R###0000@#0000#@@##@@@@@##000000R&2FPP4R#000
00###0000000000000RDDDDDDDDDDDDDD&&&D0000#@##@##00##@RDD&DRRRR#00000@R4FFP4PD@00
00#000000000000000RDDDDDDDDDD&D&D&&&DRRRRDR@@@##00#R&4&44DDDRRR#0000##R&FPP4PRR@
00##00#00000000000RDDDDDDDDDDD&&&&&&RDRRRRRRRR##@@RR&F4&RDDRR@@#0000000#&44&&4FD
000000000000000000DDDDDDD&&&DD&&&D&&R##@RRRRRRRRRRRRD&DRD&44DD&RR@#00000RDDRR4F4
000000000000000000RDDDDDDDRRDRDDD&DR@###@R&4&4444PP4DRRR&P22FF2FP&DRRRRRRRR@R4FD
000000000000000000RRRRRRR@00@@R&&DRRRRRR@&P2$33*33*4RRRDP23$*33$$**333*$$2D#@4DR
000000000000000000RRRRRRR@0000#DDRRRRRD&D4*$$%%ff3F&DRRR2$1%$$%11ff%ll1l;'IRDF&R
000000000000000000RRR@#0#00000R4444DRRRR&3llf33$32PDRR&3Ii(i%fIlI1Il!ii/' .FF4DR
0000000#0000000000#@@#0MM0M00#RP22*24DR4$l!!!I%*P4DDP31i===/lf1Illi((ii=;..*F4RR
000000000000000000#@@##0M00000@F*$%*2PFflilllI1f3*2PF3fIi/=(lf$fIi/======;iF&P4R
000000000000000000##@##00000000&3f$2F2*1i!ll1$2P4RD&4P*$$%!ii!I!ii/=/=;;;(*&R4FR
000000000000000000#####00000000D%I3$$fflil!l1$3%3&RD2PP*$%1ll!lI1%f$3fI="1RRRRDR
000000000000000000#####00000000Rf1f%II1IIl!l!!1%ff$*FFFF*%((lf**F4&D&PPFf2RRRRRR
00000000000000000M#####0000000003!i11II11l!!!(ilIIl1f$f%I="=1FP4&@#R&DP22&RRR@#@
0000000000000000000###0000000000#$i/iIIIl!!i=;"";===;""""";i12FFP4&4*4&*FRRRR@@R
000000000000000000M000000000000000R4FF1l!i(=;"''"""""'...';/i$3$f$$33$$$4DRRRRRR
000000000000000000000000000M00000000@21l(/ii/=;;=ilI;"'...'=;I%11f$$$ffP@@RR@R#0
000000000000000000000000000M00000000&11!=/li(i!I%%Ii;... .";"!I!!I1ff$*R0#@#0000
000000000000000000000000M0000M000000$lli=//=/i!lIi/l%I/""=//'=Iiiil%f$PR@#@00000
000000000000000000000000M0M0M00000MRl!!!/=;;/l%fl!lI%3233$ff%11II%%f*2PP@##00000
00000000000000000M000000M0MM00000003=illl!!iI%11%%f%f$32FF22$1%ff$f2DD4PR0000000
00000000000000000M00M0#RRRRRRRRRRRPi/(lllI1I(=;=iI$2222FPFFP*$fff%FRR2322&R00000
00000000000000000MM00#RRD&&&&&444*l/(i!ll!i!==/(il%$33*2FFF23$ff*4DDDF33*22&0000
0000000000000000000000@R@@@@@#&f!((/i!!!lI!i=;;/!l1f32*3$$$ff$2&RR@###@##00##000
0000000000000000#RRRR@@4F4R0R3!////(iii!!l%Ii=;"=l%f$333$ff%$4############00000#
0000000000000000000000#@@@#0Di/ii(=/(i!!!lI%%1!(iI%$**$$fffF@0000000000000000000
00000000000000000000000000000R3l((/((ii!lll!I%$3$f$$$$$3$f20M0000000000000000000
000000000000000000000000000000#2l///((!!l!!i!!1%f%f$3$f%%fR000000000000000000000
00000000000000#00000000000000000&l//(i!llllII1%%%1I1ff1I1&000000000000000000#00#
M00000000000000000000000000000000R3l!!llII111%%%%%%f$$f%2000000000000000000RR00#
0000#000000000000000000000000000000R*%1II111%%ffff$33fffR000000000000000000##00#
0####000000000000000000000000000000M0RF3ff$$$fff$$33$ff&00000000000#000000##000R
#00000000#00000000000000000000000000000R4FFFF22*****3fF00000000000000000000000@&
00###000000000000000000000000000000000000@RRDD&&&4P*34#00000000000000000000000#R
00000####000000000000000000000000000000000000##@RRDDR00000000000000M0000000000##
00000000#000000000000000000000000000000000000000000000000000000000000000000000##
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.
来源:https://stackoverflow.com/questions/26347985/generate-an-image-from-arbitrary-strings