Advertisement
Guest User

Untitled

a guest
Apr 1st, 2012
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.37 KB | None | 0 0
  1. #!/usr/bin/python2
  2. import sys
  3. NAME = sys.argv[0]
  4. VERSION = "0.0.1"
  5.  
  6. HELP = """ {0} : An ASCII art generator. Version {1}
  7. Usage:
  8.    {0} [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] [-c] image_filename
  9.  
  10. Commands:
  11.    -b | --block            Change the block size used for grouping pixels. This is the width of the block; the height is calculated by multiplying the block size by the aspect ratio.
  12.    -p | --pixel    Change the font character aspect ratio. By default this is 11:5, which seems to look nice. Change it based on the size of your font. Argument is specified in the format "WIDTH:HEIGHT". The colon is important.
  13.    -c | --color          Use color codes in the output. {0} uses VT100 codes by default, limiting it to 8 colors, but this might be changed later.
  14.  
  15.    -h | --help            Shows this help.
  16. """.format(NAME, VERSION)
  17.  
  18. NO_IMAGE = \
  19.     """ Usage: %s [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] image_filename """ % (NAME)
  20.  
  21. import math
  22.  
  23. CAN_HAS_PYGAME = False
  24. try:
  25.     import pygame
  26. except ImportError:
  27.     sys.stderr.write("Can't use Pygame's image handling! Unable to proceed, sorry D:\n")
  28.     exit(-1)
  29.  
  30. VT100_COLOURS = {"000": "",
  31.                  "001": "",
  32.                  "010": "",
  33.                  "011": "",
  34.                  "100": "",
  35.                  "101": "",
  36.                  "110": "",
  37.                  "111": "",
  38.                  "blank": ""}
  39.  
  40. VT100_COLOURS_I = {"000": "",
  41.                    "001": "",
  42.                    "010": "",
  43.                    "011": "",
  44.                    "100": "",
  45.                    "101": "",
  46.                    "110": "",
  47.                    "111": "",
  48.                    "blank": ""}
  49.  
  50. # Convenient debug function.
  51. DO_DEBUG = True
  52. def debug(*args):
  53.     if not DO_DEBUG: return # Abort early, (but not often).
  54.     strrep = ""
  55.     for ii in args:
  56.         strrep += str(ii)
  57.     sys.stderr.write(strrep + "\n") # Write it to stderr. Niiicce.
  58.  
  59. # System init.
  60. def init():
  61.     """ Start the necessary subsystems. """
  62.     pygame.init() # This is the only one at the moment...
  63.  
  64. # Get a section of the surface.
  65. def getSubsurface(surf, x, y, w, h):
  66.     try:
  67.         return surf.subsurface(pygame.Rect(x, y, w, h))
  68.     except ValueError as er:
  69.         return getSubsurface(surf, x, y, w - 2, h - 2)
  70.  
  71. # The main class.
  72. class AAGen:
  73.     """ A class to turn pictures into ASCII "art". """
  74.     def  __init__(self):
  75.         """ Set things up for a default conversion. """
  76.  
  77.         # Various blob settings.
  78.         self.aspectRatio = 11.0 / 5.0 # The default on my terminal.
  79.         self.blobW = 12 # The width. Also, the baseline for aspect ratio.
  80.         self.blobH = self.aspectRatio * self.blobW # The height.
  81.  
  82.         self.blobList = []
  83.         self.cat = None # The currently open file.
  84.         self.chars = """#@%H(ks+i,. """ # The characters to use.
  85.        
  86.         self.colour = False # Do we use colour?
  87.  
  88.     def processArgs(self):
  89.         """ Process the command line arguments, and remove any pertinent ones. """
  90.         cc = 0
  91.         for ii in sys.argv[1:]:
  92.             cc += 1
  93.  
  94.             if ii == "-b" or ii == "--blob":
  95.                 self.setBlob(int(sys.argv[cc + 1]))
  96.  
  97.             elif ii == "-p" or ii == "--pixel-aspect":
  98.                 jj = sys.argv[cc + 1]
  99.                 self.setAspect(float(jj.split(":")[1]) / float(jj.split(":")[0]))
  100.             elif ii == "-c" or ii == "--colour":
  101.                 self.colour = True
  102.                
  103.             elif ii == "-h" or ii == "--help":
  104.                 print(HELP)
  105.                 exit(0)
  106.        
  107.         if len(sys.argv) == 1:
  108.             print(NO_IMAGE)
  109.             exit(0)
  110.  
  111.     def setBlob(self, blobW):
  112.         """ Set the blob size. """
  113.         self.blobW = blobW
  114.         self.blobH = int(math.ceil(self.aspectRatio * self.blobW))
  115.  
  116.     def setAspect(self, aspect):
  117.         """ Set the aspect ratio. Also adjust the blob height. """
  118.         self.aspectRatio = aspect
  119.         self.blobH = int(math.ceil(self.blobW * self.aspectRatio))
  120.  
  121.     def loadImg(self, fname):
  122.         """ Loads an image into the store. """
  123.         try:
  124.             tmpSurf = pygame.image.load(fname)
  125.         except:
  126.             print("Either this is an unsupported format, or we had problems loading the file.")
  127.             return None
  128.         self.cat = tmpSurf.convert(32)
  129.        
  130.         if self.cat == None:
  131.             sys.stderr.write("Problem loading the image %s. Can't convert it!\n"
  132.                              % fname)
  133.             return None
  134.    
  135.     def makeBlob(self, section):
  136.         """ Blob a section into a single ASCII character."""
  137.         pxArr = pygame.surfarray.pixels3d(section)
  138.         colour = [0, 0, 0]
  139.         size = 0 # The number of pixels.
  140.  
  141.         # Get the density/colours.
  142.         for i in pxArr:
  143.             for j in i:
  144.                 size += 1
  145.                 # Add to the colour.
  146.                 colour[0] += j[0]
  147.                 colour[1] += j[1]
  148.                 colour[2] += j[2]
  149.  
  150.         # Get just the greyscale.
  151.         grey = apply(lambda x, y, z: (x + y + z) / 3 / size,
  152.                      colour)
  153.        
  154.         if self.colour:
  155.             # Get the 3 bit colour.
  156.             threshold = 128
  157.             nearest = ""
  158.             nearest += "1" if (colour[0] / size > threshold) else "0"
  159.             nearest += "1" if (colour[1] / size > threshold) else "0"
  160.             nearest += "1" if (colour[2] / size > threshold) else "0"
  161.  
  162.             return VT100_COLOURS[nearest], grey
  163.  
  164.         return grey
  165.  
  166.         # We just use a nasty mean function to find the average value.
  167. #        total = 0
  168. #        for pix in pxArr.flat:
  169. #            total += pix # flat is the array as a single-dimension one.
  170.  
  171. #        return total / pxArr.size # This is a bad way to do it, it loses huge amounts of precision with large blob size. However, with ASCII art...
  172.  
  173.     def getBlobs(self):
  174.         """ Get a list of blob locations. """
  175.         self.blobList = [] # Null it out.
  176.         width, height = self.cat.get_width(), self.cat.get_height()
  177.  
  178.         # If the image is the wrong size for blobs, add extra space.
  179.         if height % self.blobH != 0 or width % self.blobW != 0:
  180.             oldimg = self.cat
  181.             newW = width - (width % self.blobW) + self.blobW
  182.             newH = height - (height % self.blobH) + self.blobH
  183.             self.cat = pygame.Surface((newW, newH))
  184.  
  185.             self.cat.fill((255, 255, 255))
  186.             self.cat.blit(oldimg, pygame.Rect(0, 0, newW, newH))
  187.  
  188.         # Loop over subsections.
  189.         for row in range(0, height, int(self.blobH)):
  190.             rowItem = []
  191.             for column in range(0, width, self.blobW):
  192.                 # Construct a Rect to use.
  193.                 src = pygame.Rect(column, row, self.blobW, self.blobH)
  194.                 # Now, append the reference.
  195.                 rowItem.append(self.cat.subsurface(src))
  196.             self.blobList.append(rowItem)
  197.  
  198.         return self.blobList
  199.  
  200.     def getCharacter(self, value, colour = False):
  201.         """ Get the correct character for a pixel value. """
  202.         col = value[0] if colour else ""
  203.         value = value[1] if colour else value
  204.         if not 0 <= value <= 256:
  205.             sys.stderr.write("Incorrect pixel data provided! (given %d)\n"
  206.                              % value)
  207.             return "E"
  208.         char = self.chars[int(math.ceil(value / len(self.chars))) % len(self.chars)]
  209.         return char + col
  210.  
  211.     def convertImage(self, fname):
  212.         """ Convert an image, and print it. """
  213.         self.loadImg(fname)
  214.         self.getBlobs()
  215.  
  216.         pval = "" # The output value.
  217.         # Loop and add characters.
  218.         for ii in converter.blobList:
  219.             for jj in ii:
  220.                 ch = self.makeBlob(jj)
  221.                 pval += self.getCharacter(ch, self.colour) # Get the character.
  222.             # Reset the colour at the end of the line.
  223.             if self.colour: pval += VT100_COLOURS["blank"]
  224.  
  225.             pval += "\n" # Split it up by line.
  226.         pval = pval[:-1] # Cut out the final newline.
  227.  
  228.         print(pval) # Print it.
  229.  
  230. # Main program execution.
  231. if __name__ == "__main__":
  232.     init()
  233.  
  234.     converter = AAGen()
  235.  
  236.     converter.processArgs()
  237.     converter.convertImage(sys.argv[-1])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement