#---------------------------------------------------------------------------------------- # Post-It Posterizer # Version 1.1 # # Previously named "ImageManip.py" # # This program prepares a "Post-It" banner from a GIF file. The GIF should # consist of a small number of colors for which Post-Its are available. # It reads the GIF and breaks it into "pages" which are rectangular grids of # square Post-Its. It produces the following outputs: # - a listing of the color palette for the GIF # - a file with one line per page, listing the pixels for that page as indices # in the color palette # - a file with one line per page, listing the pixels using run-length encoding # - a "composite file" with one line for page, listing the pixels for that page # first as indices in the color pallette, and again using run-length encoding. # The composite file is in CSV format # - an HTML file that shows the color table, with counts of each color used. # # The output files are suitable for use in a mail-merge application that # produces an individual set of instructions for each page. Because the number # of entries in the files using run-length encoding may differ for each # line, these files may need to be opened and saved as Excel sheets in order to # be used with Word's mail merge feature. # # For example: # - If using 3"x3" Post-Its for the banner, a standard letter # sized page (8-1/2" x 11") can be covered by 12 Post-Its. In landscape mode, # the page would be 4 pixels wide by 3 pixels tall (12" x 9"), wich some # of the surface area of the Post-Its extending beyond the page. # - If using 2"x2" Post-Its, a good page size is half a letter page # (8-1/2" wide by 5-1/2" high in landscape mode). This can be covered # by 15 Post-Its, 5 across and 3 down, measuring 10" by 6". # # For each page, it prints the indices of the pixels in column-major order. # It also prints header info (#rows, #columns, and palette colors) in both files. # The program pads the image file as needed so that each page has a full # number of Post-Its on it. # # Columns are lettered, starting at A, and rows are numbered, starting at 1. # # This program uses Pillow, a fork of the Python Imaging Library (PIL) # # JL Popyack, August 2015, February 2016, April 2016, August-September 2016 # # v 1.1: August 2017 - revised method for recognizing and refraining from printing # colors not used in the GIF image. # #---------------------------------------------------------------------------------------- from __future__ import print_function from PIL import Image import os.path #---------------------------------------------------------------------------------------- # Constants #---------------------------------------------------------------------------------------- EXPANSION_FACTOR = 6 # Number of actual pixels used to display on screen PAGE_WIDTH = 5 # Number of Post-It's per width of page PAGE_HEIGHT = 3 # Number of Post-It's per height of page #---------------------------------------------------------------------------------------- # chunk(seq, n) creates a list of n-tuples from a list # from http://bytes.com/topic/python/answers/444697-retrieve-gifs-palette-entries-using-python-imaging-library-pil #---------------------------------------------------------------------------------------- def chunk(seq, size): return [seq[i:i+size] for i in range(0, len(seq), size)] #---------------------------------------------------------------------------------------- # get image file to convert #---------------------------------------------------------------------------------------- fileToConvert = raw_input("Name of file to convert? ") #fileToConvert = r"Banner_PhillySciFest_v4.gif" # hard-coded filename #fileToConvert = r"RobotSpringboard_2016.gif" # hard-coded filename im = Image.open(fileToConvert) #---------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------- # Output some statistics about the image file #---------------------------------------------------------------------------------------- print("Image Format: ",im.format, "; Image mode: ", im.mode, "Original image size: ", im.size) #---------------------------------------------------------------------------------------- # The image needs to be a proper multiple of the basic page size specified in # PAGE_WIDTH and PAGE_HEIGHT. # If it is not, the image will be resized by adding extra rows and/or columns. # An option to crop the image to a usable size has been commented out below. #---------------------------------------------------------------------------------------- maxWidth = (im.width/PAGE_WIDTH)*PAGE_WIDTH maxHeight = (im.height/PAGE_HEIGHT)*PAGE_HEIGHT mustResize = False if maxWidth < im.width: maxWidth = maxWidth + PAGE_WIDTH mustResize = True if maxHeight < im.height: maxHeight = maxHeight + PAGE_HEIGHT mustResize = True print("\nUsable size: ",(maxWidth,maxHeight)) ###---------------------------------------------------------------------------------------- ### Crop the image to the usable size. ### This feature has been disabled, but uncomment to re-enable. ###---------------------------------------------------------------------------------------- ##im = im.crop((0,0,maxWidth,maxHeight)) ##print("After cropping, Image size: ", im.size, "; Image mode: ", im.mode) #---------------------------------------------------------------------------------------- # Expand the image to the usable size if needed. # Select an appropriate color for the additional pixels added as padding #---------------------------------------------------------------------------------------- if mustResize: # newImage = Image.new("RGB", (maxWidth,maxHeight)) #default is "black" #newImage = Image.new("RGB", (maxWidth,maxHeight), "pink") newImage = Image.new("RGB", (maxWidth,maxHeight), "white") #------------------------------------------------------------------------------------ # Padding will be added uniformly to top and bottom of image #------------------------------------------------------------------------------------ h = int((maxWidth - im.width)/2) v = int((maxHeight - im.height)/2) newImage.paste(im, (h,v,h+im.width,v+im.height)) #------------------------------------------------------------------------------------ # A new image file is created, which contains the padded pixels #------------------------------------------------------------------------------------ fileToConvert = "expanded_"+fileToConvert #uses file extension to determine type newImage.save(fileToConvert) print("After expanding, Image size: ", im.size, "; Image mode: ", im.mode) im = Image.open(fileToConvert) filepath = os.path.dirname(fileToConvert) filename = os.path.basename(fileToConvert) if filepath != "": filepath = filepath + "/" print("filepath="+filepath+", filename="+filename) #---------------------------------------------------------------------------------------- # Print some statistics #---------------------------------------------------------------------------------------- bytes = im.tobytes() print("#Bytes: ",len(bytes)) data = list(im.getdata()) print("Data length: ",len(data)) #---------------------------------------------------------------------------------------- # Display the image (actual size) #---------------------------------------------------------------------------------------- im.show() #---------------------------------------------------------------------------------------- # Display the image, replacing each pixel with a nxn square, where n=EXPANSION_FACTOR #---------------------------------------------------------------------------------------- out = im.resize((im.width*EXPANSION_FACTOR, im.height*EXPANSION_FACTOR)) out.show() out.save(filepath + "Enlarged_"+filename) #---------------------------------------------------------------------------------------- # Convert image to image definition files for use in Post-It Pandemonium exercise # Use the filename prefix as part of the output file names # Three output files are created: # - one with the uncompressed data, one line per page # - one with the compressed data, one line per page # - one with both uncompressed and compressed data, one line per page #---------------------------------------------------------------------------------------- print("Processing ", filename) filebase = filename.split('.')[0] f = open(filepath + 'Post-It_'+filebase+'_uncompressed.txt', 'w') g = open(filepath + 'Post-It_'+filebase+'_compressed.txt', 'w') fg = open(filepath + 'Post-It_'+filebase+'_composite.csv', 'w') cf = open(filepath + 'ColorMap_'+filebase+'.txt', 'w') ch = open(filepath + 'ColorMap_'+filebase+'.html', 'w') print("\n
\n\n\n\n| Index | \nRGB Values | \nColor | \nCount | \n
|---|---|---|---|
| "+str(count)+" | \n", end="",file=ch) tempstr = " | " # remove last comma print("", file=cf) print(" | ",file=ch) print(tempstr+" ", file=ch) print(""+str(data.count(count))+" | ", file=ch) print("