def __init__(self, photobooth): self.photobooth = photobooth self.screen = photobooth.get_pygame_screen() self.buttonhandler = photobooth.get_button_handler() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) screen_colour_fill(self.screen, config.white_colour)
def __init__(self, photobooth): self.menu_text = "Create photo animation" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.gif_delay = 100 self.photo_width = 500 self.capture_delay = 2 self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler)
def __init__(self, photobooth): self.menu_text = "Take continuous photos" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler) self.total_pics = 17280 # 24 hours of a photo every 5 seconds self.capture_delay = 5 self.difference_threshold = 40
def __init__(self, photobooth): self.menu_text = "Take official profile photo (for Learn etc.)" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler) # Set image definitions - width, height, dpi self.image_defs = [['learn', 150, 150, 300], ['pure', 160, 185, 300], ['eevec', 100, 150, 300], ['office365', 300, 300, 300]]
def __init__(self, photobooth): self.menu_text = "Take accompanied photo" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler) self.chosen_accompaniment = 0 self.accompaniment_dir = self.filehandler.get_full_path( config.images_dir, 'accompany') self.accompany_button_overlay_image = self.filehandler.get_full_path( config.images_dir, 'accompany_button_overlay.png')
class Menus(object): 'A class to handle the Main Menu' photobooth = None buttonhandler = None # Set the text attributes for the main menu heading heading_font_colour = config.blue_colour # Set the text attributes for the menu item display menu_font_size = 64 menu_font_colour = config.black_colour menu_item_alignment = "lm" menu_item_position = 20 menu_cursor_font_size = 64 menu_cursor_font_colour = config.blue_colour menu_item_line_spacing = 20 menu_option_rects = None # menu_objects holds instances of each Photo Booth function class that # is added to the Main Menu menu_objects = [] def __init__(self, photobooth): self.photobooth = photobooth self.screen = photobooth.get_pygame_screen() self.buttonhandler = photobooth.get_button_handler() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) screen_colour_fill(self.screen, config.white_colour) def __del__(self): print "Destructing Menus instance" self.photobooth = None def add_main_menu_item(self, item_class): self.menu_objects.append(item_class) def display_main_menu(self): self.text_defs = [] self.image_defs = [] # Print the heading on the screen self.textprinter.print_text( [["Welcome to the Photo Booth", 120, self.heading_font_colour, "ct", 5]], 0, True ) # Print the main menu items for item_class in self.menu_objects: self.text_defs.append( [item_class.get_menu_text(), self.menu_font_size, self.menu_font_colour, self.menu_item_alignment, self.menu_item_position] ) self.menu_option_rects = self.textprinter.print_text( self.text_defs, self.menu_item_line_spacing, False ) # Print the image overlays onto the screen self.image_defs = [ [config.go_up_overlay_image, 'lb', 0, 0], [config.go_down_overlay_image, 'rb', 0, 0], [config.select_overlay_image, 'cb', 0, 0] ] self.imageprinter.print_images(self.image_defs, False) def get_main_menu_selection(self): self.cursorprinter = CursorPrinter(self.screen, self.menu_cursor_font_size, self.menu_cursor_font_colour) self.menu_choice = 0 # Print the initial cursor at the first menu option self.cursorprinter.print_cursor(self.menu_option_rects, self.menu_choice) while True: self.button = self.buttonhandler.wait_for_buttons('lsr', False) if self.button == 'l': if self.menu_choice > 0: self.menu_choice -= 1 self.cursorprinter.print_cursor( self.menu_option_rects, self.menu_choice) if self.button == 'r': if self.menu_choice < len(self.menu_option_rects) - 1: self.menu_choice += 1 self.cursorprinter.print_cursor( self.menu_option_rects, self.menu_choice) if self.button == 's': self.buttonhandler.light_button_leds('lsr', False) break if self.button == 'exit': # The user pressed the exit button - how long did they keep it pressed for? self.start_time = time.time() time.sleep(0.2) self.menu_choice = -1 # -1 indicates a short exit button while self.buttonhandler.button_is_down(config.button_pin_exit): time.sleep(0.2) # If the exit button is held down for longer than 3 seconds # then record a 'long exit button press' if time.time() - self.start_time > 3: self.menu_choice = -2 # -2 indicates a long exit button press break break # If we have been sitting at the Main Menu for longer than screen_saver_seconds secs # then go into screen_saver mode. if self.button == 'screensaver': print "Monitor going into screen saver mode." self.photobooth.screen_saver() # HACK pass return self.menu_choice def get_menu_object_at_index(self, object_index): return self.menu_objects[object_index]
class AnimatedPhoto(PhotoBoothFunction): 'Class to take a series of photographs and combine them into an animated gif' def __init__(self, photobooth): self.menu_text = "Create photo animation" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.gif_delay = 100 self.photo_width = 500 self.capture_delay = 2 self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler) def start(self, total_pics=PhotoBoothFunction.total_pics): # Take and display photos self.total_pics = total_pics # Display the instructions for this photobooth function total_pics_msg = str(self.total_pics) + " photo" if self.total_pics > 1: total_pics_msg += "s" self.instructions = [ total_pics_msg + " will be taken", "(red light will appear before each photo)", "The photos are then animated together", "Press the Start button to begin" ] # Outer loop: Instructions screen while True: choice = self.display_instructions() # If the user selected Exit, bail out if choice == "l": break while True: self.take_photos() # NOTE: If you change any part of this text display, mirror changes in erase print below text_rect_list = self.textprinter.print_text([["Processing. Please wait ...", 84, config.white_colour, "cb", 10]], 0, False) # Convert the images into an animated GIF (in a separate thread) gif_thread = threading.Thread(target=self.process_photos) gif_thread.start() # Now display the original images one after the other in separate thread, # to give the impression they are animated # (easier then trying to display the animated GIF in pygame?) display_thread_stop = threading.Event() display_thread = threading.Thread(target=self.photohandler.show_photos_animated, args=(self.image_extension, display_thread_stop)) display_thread.start() # Wait until the thread that's creating and uploading the GIF finishes gif_thread.join() # Overwrite the 'Processing' message in black to erase it screen_colour_fill(self.screen, config.black_colour, text_rect_list[0]) #self.textprinter.print_text([["Processing. Please wait ...", 84, config.black_colour, "cb", 10]], # 0, False) # Print the button label overlays self.imageprinter.print_images([[config.start_overlay_image_bk_black, 'cb', 0, 0]], False) self.imageprinter.print_images([[config.exit_side_overlay_image_bk_black, 'lb', 0, 0]], False) # Wait for the user to click Exit or Start choice = "" while True: choice = self.buttonhandler.wait_for_buttons('ls', True) if choice != 'screensaver': break # Now we can stop the thread that's animating the images on the screen display_thread_stop.set() display_thread.join() # If user pressed Exit button, return to Photo Animation Instruction screen if choice == 'l': break def take_photos(self): ################################# Step 1 - Initial Preparation ########################## super(AnimatedPhoto, self).take_photos() ################################# Step 2 - Setup camera ################################# pixel_width = self.photo_width pixel_height = self.photohandler.get_aspect_ratio_height(pixel_width) self.camera.resolution = (pixel_width, pixel_height) ################################# Step 3 - Start camera preview ######################## screen_colour_fill(self.screen, config.black_colour) self.camera.start_preview() ################################# Step 4 - Prepare the user ######################## time.sleep(self.prep_delay_short) ################################# Step 5 - Take Photos ################################ self.take_photos_and_close_camera(self.capture_delay) def process_photos(self): self.combine_into_gif() self.create_thumbnail() self.upload_photos() # Create a thumbnail of the first image in the series def create_thumbnail(self): thumb_width = 200 file_list = self.filehandler.get_sorted_file_list(os.path.join(self.local_file_dir, '*' + self.image_extension)) img = self.photohandler.resize_image(file_list[0], thumb_width, thumb_width) img.save(os.path.join(self.local_file_dir, 'photobooth_thumb' + self.image_extension)) def upload_photos(self): remote_upload_dir = time.strftime("%d-%b-%Y_A") if self.booth_id is not "": remote_upload_dir = self.booth_id + "_" + remote_upload_dir file_suffix = time.strftime("%H-%M-%S") file_defs = [ # Upload the animated GIF [os.path.join(self.local_upload_file_dir, '*' + self.animated_image_extension), 'photobooth_photo_' + file_suffix, os.path.join(self.remote_file_dir, remote_upload_dir), 1, True], # Upload just the first of the photo files [os.path.join(self.local_file_dir, 'photobooth_thumb' + self.image_extension), 'photobooth_photo_' + file_suffix, os.path.join(self.remote_file_dir, remote_upload_dir), 1, True], # Upload the HTML files to handle the animated photos [os.path.join('html', 'individual', 'index-animated.php'), 'index', os.path.join(self.remote_file_dir, remote_upload_dir), 1, True], [os.path.join('html', 'individual', 'img-animated.php'), '', os.path.join(self.remote_file_dir, remote_upload_dir), 1, True], # Make sure the base .htaccess and index files are in place [os.path.join('html', 'index.php'), '', self.remote_file_dir, 1, True], [os.path.join('html', 'redirect.html'), '', self.remote_file_dir, 1, True], [os.path.join('html', '.htaccess'), '', self.remote_file_dir, 1, True], # Make sure that all common files are in place [os.path.join('html', 'common', '*'), '', os.path.join(self.remote_file_dir, 'common'), 0, True], ] success = self.upload_photos_using_defs(file_defs) if success: return remote_upload_dir else: return None def combine_into_gif(self): local_file_dir = self.filehandler.get_local_file_dir() print "Creating animated GIF ..." # Thanks to drumminhands graphicsmagick = ("gm convert -delay " + str(self.gif_delay) + " " + local_file_dir + "/*.jpg " + self.local_upload_file_dir + "/photobooth.gif") os.system(graphicsmagick) #make the .gif
class AnimatedPhoto(PhotoBoothFunction): 'Class to take a series of photographs and combine them into an animated gif' def __init__(self, photobooth): self.menu_text = "Create photo animation" self.booth_id = photobooth.get_booth_id() self.screen = photobooth.get_pygame_screen() self.filehandler = photobooth.get_file_handler() self.buttonhandler = photobooth.get_button_handler() self.local_file_dir = self.filehandler.get_local_file_dir() self.local_upload_file_dir = self.filehandler.get_upload_file_dir() self.remote_file_dir = self.filehandler.get_remote_file_dir() self.gif_delay = 100 self.photo_width = 500 self.capture_delay = 2 self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) self.photohandler = PhotoHandler(self.screen, self.filehandler) def start(self, total_pics=PhotoBoothFunction.total_pics): # Take and display photos self.total_pics = total_pics # Display the instructions for this photobooth function total_pics_msg = str(self.total_pics) + " photo" if self.total_pics > 1: total_pics_msg += "s" self.instructions = [ total_pics_msg + " will be taken", "(red light will appear before each photo)", "The photos are then animated together", "Press the Start button to begin" ] # Outer loop: Instructions screen while True: choice = self.display_instructions() # If the user selected Exit, bail out if choice == "l": break while True: self.take_photos() # NOTE: If you change any part of this text display, mirror changes in erase print below text_rect_list = self.textprinter.print_text([[ "Processing. Please wait ...", 84, config.white_colour, "cb", 10 ]], 0, False) # Convert the images into an animated GIF (in a separate thread) gif_thread = threading.Thread(target=self.process_photos) gif_thread.start() # Now display the original images one after the other in separate thread, # to give the impression they are animated # (easier then trying to display the animated GIF in pygame?) display_thread_stop = threading.Event() display_thread = threading.Thread( target=self.photohandler.show_photos_animated, args=(self.image_extension, display_thread_stop)) display_thread.start() # Wait until the thread that's creating and uploading the GIF finishes gif_thread.join() # Overwrite the 'Processing' message in black to erase it screen_colour_fill(self.screen, config.black_colour, text_rect_list[0]) #self.textprinter.print_text([["Processing. Please wait ...", 84, config.black_colour, "cb", 10]], # 0, False) # Print the button label overlays self.imageprinter.print_images( [[config.start_overlay_image_bk_black, 'cb', 0, 0]], False) self.imageprinter.print_images( [[config.exit_side_overlay_image_bk_black, 'lb', 0, 0]], False) # Wait for the user to click Exit or Start choice = "" while True: choice = self.buttonhandler.wait_for_buttons('ls', True) if choice != 'screensaver': break # Now we can stop the thread that's animating the images on the screen display_thread_stop.set() display_thread.join() # If user pressed Exit button, return to Photo Animation Instruction screen if choice == 'l': break def take_photos(self): ################################# Step 1 - Initial Preparation ########################## super(AnimatedPhoto, self).take_photos() ################################# Step 2 - Setup camera ################################# pixel_width = self.photo_width pixel_height = self.photohandler.get_aspect_ratio_height(pixel_width) self.camera.resolution = (pixel_width, pixel_height) ################################# Step 3 - Start camera preview ######################## screen_colour_fill(self.screen, config.black_colour) self.camera.start_preview() ################################# Step 4 - Prepare the user ######################## time.sleep(self.prep_delay_short) ################################# Step 5 - Take Photos ################################ self.take_photos_and_close_camera(self.capture_delay) def process_photos(self): self.combine_into_gif() self.create_thumbnail() self.upload_photos() # Create a thumbnail of the first image in the series def create_thumbnail(self): thumb_width = 200 file_list = self.filehandler.get_sorted_file_list( os.path.join(self.local_file_dir, '*' + self.image_extension)) img = self.photohandler.resize_image(file_list[0], thumb_width, thumb_width) img.save( os.path.join(self.local_file_dir, 'photobooth_thumb' + self.image_extension)) def upload_photos(self): remote_upload_dir = time.strftime("%d-%b-%Y_A") if self.booth_id is not "": remote_upload_dir = self.booth_id + "_" + remote_upload_dir file_suffix = time.strftime("%H-%M-%S") file_defs = [ # Upload the animated GIF [ os.path.join(self.local_upload_file_dir, '*' + self.animated_image_extension), 'photobooth_photo_' + file_suffix, os.path.join(self.remote_file_dir, remote_upload_dir), 1, True ], # Upload just the first of the photo files [ os.path.join(self.local_file_dir, 'photobooth_thumb' + self.image_extension), 'photobooth_photo_' + file_suffix, os.path.join(self.remote_file_dir, remote_upload_dir), 1, True ], # Upload the HTML files to handle the animated photos [ os.path.join('html', 'individual', 'index-animated.php'), 'index', os.path.join(self.remote_file_dir, remote_upload_dir), 1, True ], [ os.path.join('html', 'individual', 'img-animated.php'), '', os.path.join(self.remote_file_dir, remote_upload_dir), 1, True ], # Make sure the base .htaccess and index files are in place [ os.path.join('html', 'index.php'), '', self.remote_file_dir, 1, True ], [ os.path.join('html', 'redirect.html'), '', self.remote_file_dir, 1, True ], [ os.path.join('html', '.htaccess'), '', self.remote_file_dir, 1, True ], # Make sure that all common files are in place [ os.path.join('html', 'common', '*'), '', os.path.join(self.remote_file_dir, 'common'), 0, True ], ] success = self.upload_photos_using_defs(file_defs) if success: return remote_upload_dir else: return None def combine_into_gif(self): local_file_dir = self.filehandler.get_local_file_dir() print "Creating animated GIF ..." # Thanks to drumminhands graphicsmagick = ("gm convert -delay " + str(self.gif_delay) + " " + local_file_dir + "/*.jpg " + self.local_upload_file_dir + "/photobooth.gif") os.system(graphicsmagick) #make the .gif
class Menus(object): 'A class to handle the Main Menu' photobooth = None buttonhandler = None # Set the text attributes for the main menu heading heading_font_colour = config.blue_colour # Set the text attributes for the menu item display menu_font_size = 64 menu_font_colour = config.black_colour menu_item_alignment = "lm" menu_item_position = 20 menu_cursor_font_size = 64 menu_cursor_font_colour = config.blue_colour menu_item_line_spacing = 20 menu_option_rects = None # menu_objects holds instances of each Photo Booth function class that # is added to the Main Menu menu_objects = [] def __init__(self, photobooth): self.photobooth = photobooth self.screen = photobooth.get_pygame_screen() self.buttonhandler = photobooth.get_button_handler() self.textprinter = TextPrinter(self.screen) self.imageprinter = ImagePrinter(self.screen) screen_colour_fill(self.screen, config.white_colour) def __del__(self): print "Destructing Menus instance" self.photobooth = None def add_main_menu_item(self, item_class): self.menu_objects.append(item_class) def display_main_menu(self): self.text_defs = [] self.image_defs = [] # Print the heading on the screen self.textprinter.print_text([[ "Welcome to the Photo Booth", 120, self.heading_font_colour, "ct", 5 ]], 0, True) # Print the main menu items for item_class in self.menu_objects: self.text_defs.append([ item_class.get_menu_text(), self.menu_font_size, self.menu_font_colour, self.menu_item_alignment, self.menu_item_position ]) self.menu_option_rects = self.textprinter.print_text( self.text_defs, self.menu_item_line_spacing, False) # Print the image overlays onto the screen self.image_defs = [[config.go_up_overlay_image, 'lb', 0, 0], [config.go_down_overlay_image, 'rb', 0, 0], [config.select_overlay_image, 'cb', 0, 0]] self.imageprinter.print_images(self.image_defs, False) def get_main_menu_selection(self): self.cursorprinter = CursorPrinter(self.screen, self.menu_cursor_font_size, self.menu_cursor_font_colour) self.menu_choice = 0 # Print the initial cursor at the first menu option self.cursorprinter.print_cursor(self.menu_option_rects, self.menu_choice) while True: self.button = self.buttonhandler.wait_for_buttons('lsr', False) if self.button == 'l': if self.menu_choice > 0: self.menu_choice -= 1 self.cursorprinter.print_cursor(self.menu_option_rects, self.menu_choice) if self.button == 'r': if self.menu_choice < len(self.menu_option_rects) - 1: self.menu_choice += 1 self.cursorprinter.print_cursor(self.menu_option_rects, self.menu_choice) if self.button == 's': self.buttonhandler.light_button_leds('lsr', False) break if self.button == 'exit': # The user pressed the exit button - how long did they keep it pressed for? self.start_time = time.time() time.sleep(0.2) self.menu_choice = -1 # -1 indicates a short exit button while self.buttonhandler.button_is_down( config.button_pin_exit): time.sleep(0.2) # If the exit button is held down for longer than 3 seconds # then record a 'long exit button press' if time.time() - self.start_time > 3: self.menu_choice = -2 # -2 indicates a long exit button press break break # If we have been sitting at the Main Menu for longer than screen_saver_seconds secs # then go into screen_saver mode. if self.button == 'screensaver': print "Monitor going into screen saver mode." self.photobooth.screen_saver() # HACK pass return self.menu_choice def get_menu_object_at_index(self, object_index): return self.menu_objects[object_index]
def __init__(self, screen, filehandler): self.screen = screen self.filehandler = filehandler self.imageprinter = ImagePrinter(self.screen)
class PhotoHandler(object): 'Base class for image transformation code' screen = None filehandler = None imageprinter = None def __init__(self, screen, filehandler): self.screen = screen self.filehandler = filehandler self.imageprinter = ImagePrinter(self.screen) # Given a width, find the corresponding height that would fit the screen's aspect ratio def get_aspect_ratio_height(self, pixel_width): return pygame.display.Info().current_h * pixel_width // pygame.display.Info().current_w # Resize an image keeping the same aspect ratio def resize_image(self, img_filename, new_width, new_height): img = Image.open(img_filename) img_width, img_height = img.size # First, resize the image if new_width > new_height: # Required image is Landscape scale_factor = float(new_width) / float(img_width) temp_width = new_width temp_height = int(float(img_height) * float(scale_factor)) else: # Required image is Portrait (or Square) scale_factor = float(new_height) / float(img_height) temp_width = int(float(img_width) * float(scale_factor)) temp_height = new_height img = img.resize((temp_width, temp_height), Image.ANTIALIAS) return img # Thanks Charlie Clark: http://code.activestate.com/recipes/577630-comparing-two-images/ def rms_difference(self, im1, im2): "Calculate the root-mean-square difference between two images" diff = ImageChops.difference(im1, im2) h = diff.histogram() #sq = (value*(idx**2) for idx, value in enumerate(h)) sq = (value*((idx%256)**2) for idx, value in enumerate(h)) sum_of_squares = sum(sq) rms = math.sqrt(sum_of_squares / float(im1.size[0] * im1.size[1])) return rms # Convert all captured images into the formats defined in image_defs def prepare_images(self, image_extension, image_defs, copy_origs): # Get directories image_dir = self.filehandler.get_local_file_dir() # PiCamera captures images at 72 pixels/inch. # Collect a list of the original PiCamera-saved files file_pattern = os.path.join(image_dir, "photobooth*" + image_extension) files = self.filehandler.get_sorted_file_list(file_pattern) # Process the images in separate threads - in an attempt to speed the process up ... # (TODO: Threading doesn't seem to process the images any quicker) processing_thread_list = [] self.imageprinter.print_images([[files[0], "ct", 10, 50]], False) for curr_img in files: processing_thread_list.append(threading.Thread(target=self.prepare_one_image, args=(curr_img, image_defs, copy_origs))) processing_thread_list[len(processing_thread_list)-1].start() # Wait for all processing threads to finish for curr_thread in processing_thread_list: curr_thread.join() def prepare_one_image(self, image_file, image_defs, copy_origs): upload_dir = self.filehandler.get_upload_file_dir() if copy_origs: filename = os.path.basename(image_file) name, extension = os.path.splitext(filename) new_filepath = os.path.join(upload_dir, 'original-' + name + extension) self.filehandler.copy_file(image_file, new_filepath) # Do a straight copy of the image_file file # Tip from http://learnpythonthehardway.org/book/ex17.html #in_file = open(image_file) #img_data = in_file.read() #out_file = open(new_filepath, 'w') #out_file.write(img_data) #out_file.close() #in_file.close() for curr_img_def in image_defs: # Unpack the curr_img_def array into variables def_prefix, def_width, def_height, def_dpi = curr_img_def filename = os.path.basename(image_file) name, extension = os.path.splitext(filename) new_filepath = os.path.join(upload_dir, def_prefix + '-' + name + extension) img = self.resize_image(image_file, def_width, def_height) img_width, img_height = img.size # Second, if the current image def is a different aspect ratio, crop the image image_x = (img_width - def_width) // 2 image_y = (img_height - def_height) // 2 img = img.crop((image_x, image_y, image_x + def_width, image_y + def_height)) img_width, img_height = img.size # Finally save the current image, at the requested DPI img.save(new_filepath, dpi=(def_dpi, def_dpi)) # *** Display the captured images on the PyGame screen *** def show_photos_tiled(self, image_extension): # Get directories image_dir = self.filehandler.get_local_file_dir() file_pattern = os.path.join(image_dir, "*" + image_extension) files = self.filehandler.get_sorted_file_list(file_pattern) num_images = len(files) if num_images < 1: return # Find the aspect ratio of the images, for later use img = Image.open(files[0]) image_width, image_height = img.size num_row_1 = num_images // 2 # Note: given an odd number of images, fewer will appear on top row num_row_2 = num_images - num_row_1 screen_width = pygame.display.Info().current_w screen_height = pygame.display.Info().current_h # display_* is the size that we want to display the image at display_height = screen_height // 2 display_width = int(float(display_height) / float(image_height) * float(image_width)) row_1_x = (screen_width - (num_row_1 * display_width)) // 2 row_2_x = (screen_width - (num_row_2 * display_width)) // 2 image_num = 0 for f in files: image_num = image_num + 1 if image_num <= num_row_1: image_x = row_1_x + display_width * (image_num - 1) image_y = 0 else: image_x = row_2_x + display_width * (image_num - num_row_1 - 1) image_y = display_height try: img = pygame.image.load(f) img = pygame.transform.scale(img, (display_width, display_height)) self.screen.blit(img,(image_x,image_y)) except pygame.error, message: print "ERROR: Image " + os.path.basename(f) + " failed to load: " + message pygame.display.flip()
class PhotoHandler(object): 'Base class for image transformation code' screen = None filehandler = None imageprinter = None def __init__(self, screen, filehandler): self.screen = screen self.filehandler = filehandler self.imageprinter = ImagePrinter(self.screen) # Given a width, find the corresponding height that would fit the screen's aspect ratio def get_aspect_ratio_height(self, pixel_width): return pygame.display.Info( ).current_h * pixel_width // pygame.display.Info().current_w # Resize an image keeping the same aspect ratio def resize_image(self, img_filename, new_width, new_height): img = Image.open(img_filename) img_width, img_height = img.size # First, resize the image if new_width > new_height: # Required image is Landscape scale_factor = float(new_width) / float(img_width) temp_width = new_width temp_height = int(float(img_height) * float(scale_factor)) else: # Required image is Portrait (or Square) scale_factor = float(new_height) / float(img_height) temp_width = int(float(img_width) * float(scale_factor)) temp_height = new_height img = img.resize((temp_width, temp_height), Image.ANTIALIAS) return img # Thanks Charlie Clark: http://code.activestate.com/recipes/577630-comparing-two-images/ def rms_difference(self, im1, im2): "Calculate the root-mean-square difference between two images" diff = ImageChops.difference(im1, im2) h = diff.histogram() #sq = (value*(idx**2) for idx, value in enumerate(h)) sq = (value * ((idx % 256)**2) for idx, value in enumerate(h)) sum_of_squares = sum(sq) rms = math.sqrt(sum_of_squares / float(im1.size[0] * im1.size[1])) return rms # Convert all captured images into the formats defined in image_defs def prepare_images(self, image_extension, image_defs, copy_origs): # Get directories image_dir = self.filehandler.get_local_file_dir() # PiCamera captures images at 72 pixels/inch. # Collect a list of the original PiCamera-saved files file_pattern = os.path.join(image_dir, "photobooth*" + image_extension) files = self.filehandler.get_sorted_file_list(file_pattern) # Process the images in separate threads - in an attempt to speed the process up ... # (TODO: Threading doesn't seem to process the images any quicker) processing_thread_list = [] self.imageprinter.print_images([[files[0], "ct", 10, 50]], False) for curr_img in files: processing_thread_list.append( threading.Thread(target=self.prepare_one_image, args=(curr_img, image_defs, copy_origs))) processing_thread_list[len(processing_thread_list) - 1].start() # Wait for all processing threads to finish for curr_thread in processing_thread_list: curr_thread.join() def prepare_one_image(self, image_file, image_defs, copy_origs): upload_dir = self.filehandler.get_upload_file_dir() if copy_origs: filename = os.path.basename(image_file) name, extension = os.path.splitext(filename) new_filepath = os.path.join(upload_dir, 'original-' + name + extension) self.filehandler.copy_file(image_file, new_filepath) # Do a straight copy of the image_file file # Tip from http://learnpythonthehardway.org/book/ex17.html #in_file = open(image_file) #img_data = in_file.read() #out_file = open(new_filepath, 'w') #out_file.write(img_data) #out_file.close() #in_file.close() for curr_img_def in image_defs: # Unpack the curr_img_def array into variables def_prefix, def_width, def_height, def_dpi = curr_img_def filename = os.path.basename(image_file) name, extension = os.path.splitext(filename) new_filepath = os.path.join(upload_dir, def_prefix + '-' + name + extension) img = self.resize_image(image_file, def_width, def_height) img_width, img_height = img.size # Second, if the current image def is a different aspect ratio, crop the image image_x = (img_width - def_width) // 2 image_y = (img_height - def_height) // 2 img = img.crop( (image_x, image_y, image_x + def_width, image_y + def_height)) img_width, img_height = img.size # Finally save the current image, at the requested DPI img.save(new_filepath, dpi=(def_dpi, def_dpi)) # *** Display the captured images on the PyGame screen *** def show_photos_tiled(self, image_extension): # Get directories image_dir = self.filehandler.get_local_file_dir() file_pattern = os.path.join(image_dir, "*" + image_extension) files = self.filehandler.get_sorted_file_list(file_pattern) num_images = len(files) if num_images < 1: return # Find the aspect ratio of the images, for later use img = Image.open(files[0]) image_width, image_height = img.size num_row_1 = num_images // 2 # Note: given an odd number of images, fewer will appear on top row num_row_2 = num_images - num_row_1 screen_width = pygame.display.Info().current_w screen_height = pygame.display.Info().current_h # display_* is the size that we want to display the image at display_height = screen_height // 2 display_width = int( float(display_height) / float(image_height) * float(image_width)) row_1_x = (screen_width - (num_row_1 * display_width)) // 2 row_2_x = (screen_width - (num_row_2 * display_width)) // 2 image_num = 0 for f in files: image_num = image_num + 1 if image_num <= num_row_1: image_x = row_1_x + display_width * (image_num - 1) image_y = 0 else: image_x = row_2_x + display_width * (image_num - num_row_1 - 1) image_y = display_height try: img = pygame.image.load(f) img = pygame.transform.scale(img, (display_width, display_height)) self.screen.blit(img, (image_x, image_y)) except pygame.error, message: print "ERROR: Image " + os.path.basename( f) + " failed to load: " + message pygame.display.flip()