473,399 Members | 2,478 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,399 developers and data experts.

Program that takes an image (640x384) and converts it to ascii strings

kudos
127 Expert 100+
Do you remeber the "old" internet? before webbrowsers, when everything had a strong unix flavour? Back then there was something called "ascii art" that is, use the ascii letters to create an image. Here is a program that takes an image (640x384) and converts it to ascii strings. I wanted to create this image for a t-shirt

You would need to copy the "courier new" font from your font directory to the same directory as the script, and also add an image that should be converted:

Expand|Select|Wrap|Line Numbers
  1. import Image, ImageDraw
  2. import ImageFont
  3. import math
  4.  
  5. # funny useless program
  6.  
  7. lu = []
  8. for i in range(125-32):
  9.  lu.append(chr(i+33))
  10. i2 = Image.new("RGBA",(64,64))
  11. ii = Image.open("mona.png") # change this to your own picture, but it needs to be 640x384!
  12. im = Image.new("RGBA",(64,64))
  13. d = ImageDraw.Draw(im)
  14. f = ImageFont.truetype("courbi.ttf",13)
  15.  
  16. str=""
  17. for v in range(24):
  18.  print v
  19.  for k in range(80):
  20.   chr = ""
  21.   maxdiff = 0xffffff
  22.   for e in lu:
  23.    d.rectangle([0,0,64,64], fill=0)
  24.    d.text((0,0),e,font=f,fill=0xffffff)
  25.    diff = 0
  26.    r0 = 0
  27.    g0 = 0
  28.    b0 = 0
  29.    for a in range(16):
  30.     for b in range(8):
  31.      c =  im.getpixel((b,a))[0]
  32.      c0 = (ii.getpixel((b+(k*8),a+(v*16)))[0] + ii.getpixel((b+(k*8),a+(v*16)))[1] + ii.getpixel((b+(k*8),a+(v*16)))[2])/3.0
  33.      r0+= ii.getpixel((b+(k*8),a+(v*16)))[0]
  34.      g0+= ii.getpixel((b+(k*8),a+(v*16)))[1]
  35.      b0+= ii.getpixel((b+(k*8),a+(v*16)))[2]
  36.  
  37.      diff+=abs(c-c0)
  38.    if(diff < maxdiff):
  39.     maxdiff = diff
  40.     chr = e
  41.  
  42.   r0*=1.0
  43.   g0*=1.0
  44.   b0*=1.0
  45.   r0/=(16.0*8.0)
  46.   g0/=(16.0*8.0)
  47.   b0/=(16.0*8.0)
  48.   ir0 = int(r0)
  49.   ig0 = int(g0)
  50.   ib0 = int(b0)
  51.  
  52.   r1 =  hex(ir0)
  53.   r1 = r1[2:len(r1)]
  54.   g1 = hex(ig0)
  55.   g1 = g1[2:len(g1)]
  56.   b1 = hex(ib0)
  57.   b1 = b1[2:len(g1)]
  58.  
  59.   if(chr == " "):
  60.    chr="&nbsp;"
  61.  
  62.   str+="<td width=\"8px\"><font size=\"1px\" color=\"#"+r1+g1+b1+"\">"+chr+"</font></td>"
  63.  str="<tr>"+str+"</tr>"
  64.  
  65. str= "<html><body bgcolor=\"#000000\"><table width=\"640px\" height=\"386px\">"+str+"</table></body></html>"
  66. f = open("mona.txt","w")
  67. f.write(str)
  68. f.close()
  69.  
This program output it as a .html file, but it should easy to modify so it will output txt instead.

-kudos
Sep 14 '09 #1
1 6880
I have modified your code to do a few things different.
It speeds it up enormously. I took 6min down to 1sec. I also made it run based on commandline parser so you can now put this on your website or wherever you want. There are a few issues in it if I recall (its been about 6 months since I wrote this). Feel free to tune it up and bring it back for everyone to have. It also uses true luminance vals in algorithm (if wanted).

--------------------------------------
Expand|Select|Wrap|Line Numbers
  1. '''
  2.     Inspired by -Kudos code from http://bytes.com/topic/python/insights/874180-ascii-art-creator
  3.     Pretty much a rewrite, but I give kudos complete props for posting this and making it fun and easy
  4. '''
  5.  
  6.  
  7. import sys,os
  8. import Image, ImageDraw
  9. import ImageFont
  10. import math
  11. import time, random
  12. from optparse import OptionParser
  13.  
  14.  
  15. # funny useless program
  16. t0                             = time.time();
  17. VERBOSE_LEVEL                = 1;
  18. MIN_LUM_DEFAULT             = .1;
  19. MAX_LUM_DEFAULT             = 1.0;
  20. OFFSET_LUM_DEFAULT            = 0;
  21. OUTPUT_FONT_SIZE_DEFAULT    = 3;
  22. CHARMAP_DEFAULT                = 'luminance';
  23.  
  24. ROWS_DEFAULT                 = 24
  25. COLS_DEFAULT                 = 80
  26.  
  27. MAX_RGB                        = 255
  28. MAX_F_RGB                    = float(MAX_RGB)
  29.  
  30. def normalizePixel( rgb ):
  31.     return rgb[0]/MAX_F_RGB,rgb[1]/MAX_F_RGB,rgb[2]/MAX_F_RGB
  32.  
  33. def getLuminancePixel( rgb ):
  34.     nR,nG,nB = normalizePixel( rgb )
  35.     return nR*0.299 + nG*0.587 + nB*0.114
  36.  
  37. def output( val, vLevel=0 ):
  38.     if vLevel <= VERBOSE_LEVEL:
  39.         print ( val )
  40.  
  41. def main( args ):
  42.  
  43.     global VERBOSE_LEVEL;
  44.  
  45.     parser = OptionParser()
  46.     parser.add_option("-f", "--file", action='append', dest="files", help="process these files", metavar="LIST")
  47.     parser.add_option(        "--characters", action="store", dest="characters", help="characters to use")
  48.     parser.add_option(        "--character_map", action="store", dest="characterMap", help="maps characters: luminance, order, random")
  49.     parser.add_option("-r", "--rows", action="store", dest="rows", help="number of rows")
  50.     parser.add_option("-c", "--columns", action="store", dest="columns", help="number of columns")
  51.     parser.add_option(        "--min_luminance", action="store", dest="minLuminance", help="min luminance value to clip at black")
  52.     parser.add_option(        "--max_luminance", action="store", dest="maxLuminance", help="max luminance value to clip at white")
  53.     parser.add_option(        "--offset_luminance", action="store", dest="offsetLuminance", help="offset luminance value")
  54.     parser.add_option(        "--output_font_size", action="store", dest="outputFontSize", help="output font size in px")
  55.     parser.add_option('-v', "--verbose", action="store", dest="verbose", help="verbosity: 0=None, 1=completion output, 2=image info, 3=pixel blending info")
  56.  
  57.     (options, args) = parser.parse_args( args )
  58.  
  59.     files             = options.files;
  60.     characters         = options.characters;
  61.     characterMap    = options.characterMap;
  62.     rows            = options.rows;
  63.     columns            = options.columns;
  64.     minLum            = options.minLuminance;
  65.     maxLum            = options.maxLuminance;
  66.     offsetLum        = options.offsetLuminance;
  67.     outputFontSize    = options.outputFontSize;
  68.     verbose            = options.verbose;
  69.  
  70.     errors = [];
  71.     # SANITY CHECKS
  72.  
  73.     if verbose:
  74.         try:
  75.             VERBOSE_LEVEL = int(verbose);
  76.         except:
  77.             VERBOSE_LEVEL = VERBOSE_LEVEL;
  78.  
  79.     if not files:
  80.         errors.append('Failed to get files: make sure you specify files to use');
  81.     if not characters:
  82.         characters = []
  83.         for i in range( 125-32):
  84.             characters.append( chr( i + 33) );
  85.     else:
  86.         characters = str(characters);
  87.  
  88.     if characterMap:
  89.         if characterMap.lower() in ('luminance','order','random'):
  90.             characterMap = characterMap.lower();
  91.         else:
  92.             characterMap = CHARMAP_DEFAULT;
  93.     else:
  94.         characterMap = CHARMAP_DEFAULT;
  95.  
  96.     try:
  97.         rows = int(rows);
  98.     except:
  99.         rows = ROWS_DEFAULT;
  100.     try:
  101.         columns = int(columns);
  102.     except:
  103.         columns = COLS_DEFAULT;
  104.  
  105.     try:
  106.         minLum     = float( minLum)
  107.     except:
  108.         minLum = MIN_LUM_DEFAULT
  109.     try:
  110.         maxLum     = float( maxLum)
  111.     except:
  112.         maxLum = MAX_LUM_DEFAULT
  113.  
  114.     try:
  115.         offsetLum     = int( offsetLum)
  116.     except:
  117.         offsetLum = OFFSET_LUM_DEFAULT
  118.  
  119.     try:
  120.         outputFontSize     = int( outputFontSize)
  121.     except:
  122.         outputFontSize = OUTPUT_FONT_SIZE_DEFAULT
  123.  
  124.     if errors:
  125.         for err in errors:
  126.             output( 'ERROR: %s'%err, 0);
  127.         return -1;
  128.  
  129.     output( 'verbose: %s'%verbose    , 3 );
  130.     output( 'files: %s'%files        , 3 );
  131.     output( 'characters: %s'%characters    , 3 );
  132.     output( 'character_map: %s'%characterMap    , 3 );
  133.     output( 'rows: %s'%rows    , 3 );
  134.     output( 'columns: %s'%columns    , 3 );
  135.     output( 'minLum: %s'%minLum    , 3 );
  136.     output( 'maxLum: %s'%maxLum    , 3 );
  137.     output( 'offsetLum: %s'%offsetLum    , 3 );
  138.     output( 'outputFontSize: %s'%outputFontSize    , 3 );
  139.  
  140.     imgChr = Image.new("RGBA",(64,64))
  141.     drwObj = ImageDraw.Draw(imgChr)
  142.     fntObj = ImageFont.truetype("courbi.ttf",13)
  143.  
  144.  
  145.     # build the value table
  146.     lumLUT_Tmp         = list();
  147.     lumLUT         = list();
  148.     lumToCharTmp     = dict();
  149.     maxLum         = 0.0;
  150.     minLum         = 1.0;
  151.     for ch in characters:
  152.  
  153.         w,h = fntObj.getsize(ch)
  154.         drwObj.rectangle([0,0,64,64], fill=0)
  155.         drwObj.text((0,0),ch,font=fntObj,fill=0xffffff)
  156.         diff = 0
  157.         r0 = 0
  158.         g0 = 0
  159.         b0 = 0
  160.  
  161.         lum = 0;
  162.         for a in range(16):
  163.             for b in range(8):
  164.                 lum += getLuminancePixel( imgChr.getpixel((b,a)) )
  165.         lum = lum/(16*8)
  166.         lumLUT_Tmp.append( lum )
  167.         lumToCharTmp[lum] = ch ;
  168.  
  169.         if lum > maxLum:
  170.             maxLum = lum;
  171.         if lum < minLum:
  172.             minLum = lum;
  173.  
  174.         output( ('ch: %s\t(%sx%s) lum:%s'%(ch,w,h,lum)), 3);
  175.     lumToChar     = dict();
  176.     for lum in lumLUT_Tmp:
  177.         nLum = (lum-minLum)/(maxLum-minLum);
  178.         lumToChar[nLum] = lumToCharTmp[lum];
  179.         lumLUT.append( nLum );
  180.  
  181.     lumLUT.sort();
  182.  
  183.     for file in files:
  184.         tImage = time.time()
  185.         imgSrc = Image.open(file) 
  186.         iw,ih = imgSrc.size;
  187.         cellW = iw/columns;
  188.         cellH = ih/rows;
  189.         #~ print '\n%sx%s  %sx%s'%(cellW,cellH, iw, ih)
  190.         sVal=""
  191.         index=0
  192.         for x in range(rows):
  193.             for y in range(columns):
  194.                 ch = ""
  195.                 maxdiff = 0xffffff
  196.  
  197.                 iLum = 0;
  198.                 r = 0;
  199.                 g = 0;
  200.                 b = 0;
  201.                 for xCell in range(cellH):
  202.                     for yCell in range(cellW):
  203.                         color = imgSrc.getpixel((yCell+(y*cellW),xCell+(x*cellH)));
  204.                         r += color[0]
  205.                         g += color[1]
  206.                         b += color[2]
  207.  
  208.                 # average        
  209.                 r = r/(cellH*cellW*1.0);
  210.                 g = g/(cellH*cellW*1.0);
  211.                 b = b/(cellH*cellW*1.0);
  212.  
  213.                 hexColor = '%.2x%.2x%.2x'%( r+offsetLum,g+offsetLum,b+offsetLum );
  214.  
  215.                 # normalize
  216.                 iLum += getLuminancePixel( (r,g,b) )
  217.  
  218.                 #~ r /= MAX_F_RGB;
  219.                 #~ g /= MAX_F_RGB;
  220.                 #~ b /= MAX_F_RGB;
  221.  
  222.                 if characterMap == 'luminance' :
  223.                     ix = 0;
  224.                     for lum in lumLUT:
  225.                         if iLum < MIN_LUM_DEFAULT:
  226.                             ch = ' ';
  227.                             break;
  228.                         if iLum > MAX_LUM_DEFAULT:
  229.                             ch = lumToChar[lumLUT[-1]];
  230.                             break;
  231.                         if lum > iLum:
  232.                             try:
  233.                                 ch = lumToChar[lumLUT[ix]];
  234.                             except:
  235.                                 ch = lumToChar[lumLUT[0]]
  236.                             break;
  237.                         ix+=1;
  238.  
  239.                 elif characterMap == 'order':
  240.                     if index >= len(characters):
  241.                         index=0;
  242.                     ch = characters[index];
  243.                 elif characterMap == 'random':
  244.                     ch = characters[int(random.random()*len(characters))];
  245.  
  246.                 index+=1
  247.                 if(ch == " "):
  248.                     ch="&nbsp;"
  249.                 #~ output( 'Character: %s'%ch, 3);
  250.                 output( ('character: %s\trow:%s column:%s\tlum: %s\tr:%s g:%s b:%s\thex_color:%s'%(ch,x,y,iLum,r,g,b,hexColor)), 3);
  251.                 sVal += '''<font size="%spx" color="#%s">%s</font></td>'''%(outputFontSize,hexColor,ch);
  252.             sVal += '<br/>';
  253.         sVal= "<html><body bgcolor=\"#000000\"><pre>"+sVal+"</pre><br/>\n<img src=\"%s\"/>\n</body></html>"%file
  254.         try:
  255.             f = open("%s.html"%file,"w")
  256.             f.write(sVal)
  257.             f.close()
  258.         except:
  259.             output('Failed to write file: %s'%file, 0 );
  260.  
  261.         output( ('%s  (%sx%s : %sx%s)'%(file,iw,ih,cellW,cellH)).center(50,'.'), 2)
  262.         output( 'IMAGE PROCESS TIME: %s (sec)\t%s'%((time.time()-tImage),os.path.basename(file)), 1);
  263.  
  264.     output( '%s\nTOTAL PROCESS TIME: %s (sec)'%('-'*100, (time.time()-t0) ), 1);
  265.  
  266. if __name__=='__main__':
  267.     args = sys.argv;
  268.  
  269.     args += [
  270.         #~ '--file','./angry_birds.jpg',
  271.         #~ '--characters','-+=@!?%$#^&*()[]{}_\\/><,.`~',
  272.         #~ '-r','80', '-c','260',
  273.         #~ '--output_font_size','1',
  274.         #~ '--character_map','random',
  275.         #~ '--character_map','order',
  276.         #~ '-v', '3',
  277.  
  278.     ]
  279.  
  280.     sys.exit( main( args ) );
  281.  
  282.  
--------------------------------------
Mar 20 '12 #2

Sign in to post your reply or Sign up for a free account.

Similar topics

1
by: Stefan Ram | last post by:
Some style guides suggest to repeat a DC.creator meta-element for multiple authors, but doesn't this contradict its specification? "An entity primarily responsible for making the content of the...
1
by: Gorilla | last post by:
I bound my package with EXPLAIN(YES), and it's got the following static SQL in it: EXEC SQL SELECT CARDF, RECLENGTH INTO :CARDF,:RECLENGTH FROM SYSIBM.SYSTABLES WHERE NAME = :TBNAME AND...
18
by: Ger | last post by:
I have not been able to find a simple, straight forward Unicode to ASCII string conversion function in VB.Net. Is that because such a function does not exists or do I overlook it? I found...
1
by: shenanwei | last post by:
I tried revoke all priviledge from user and REVOKE DROPIN ON SCHEMA Still the creator can drop the table.
0
by: Roshan | last post by:
Hi, I am trying to programatically add a FileSystemAccessRule for CREATOR OWNER to the filesystemsecurity obj of a folder whose creator and owner is a user account say 'SomeUser'. The rule gets...
0
by: anokun7 | last post by:
We are using DB2 v7 on OS/390. I have installed db2connect enterprise on my local windows XP box. Using db2connect I have created a dsn and could successfully connect using the DB2 control...
13
by: alive84 | last post by:
Hi there, I have a two problems concerning option button values on a report and data report creator reports. The situation: I have three option value boxes two have 3 option and one has...
3
by: alan | last post by:
Hello all, I'd like to know if there is a nice method of defining a functor creator which accepts an N-ary function and returns a functor based on that function. For example I have a function:...
0
by: phpFoxHelp | last post by:
Hey everyone! I am currently using phpFox to start an online community. I have it set so that there is no captcha nor email validation for new registrations. I am looking for a program that will...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.