Exemplo n.º 1
0
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]
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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 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()