def get_rect(self): """Return a Rect object (as defined in pygame) for resizing preview and images in order to fit to the defined window. """ rect = self._window.get_rect() res = sizing.new_size_keep_aspect_ratio(self.resolution, (rect.width - 2 * self._border, rect.height - 2 * self._border)) return pygame.Rect(rect.centerx - res[0] // 2, rect.centery - res[1] // 2, res[0], res[1])
def get_pygame_image(name, size=None, antialiasing=True, hflip=False, vflip=False, crop=False, angle=0, color=(255, 255, 255), bg_color=None): """Return a Pygame image. If a size is given, the image is resized keeping the original image's aspect ratio. :param name: name of an image located in language folders :type name: str :param size: resize image to this size :type size: tuple :param antialiasing: use antialiasing algorithm when resize :type antialiasing: bool :param hflip: apply an horizontal flip :type hflip: bool :param vflip: apply a vertical flip :type vflip: bool :param crop: crop image to fit aspect ration of the size :type crop: bool :param angle: angle of rotation of the image :type angle: int :return: pygame.Surface with image :rtype: object """ path = get_filename(name) if not size: image = pygame.image.load(path).convert() else: if osp.isfile(path): pil_image = Image.open(path) else: pil_image = Image.new('RGBA', size, (0, 0, 0, 0)) if color != None: pil_image = set_picto_color(pil_image, color, bg_color) if crop: pil_image = pil_image.crop( sizing.new_size_by_croping_ratio(pil_image.size, size)) pil_image = pil_image.resize( sizing.new_size_keep_aspect_ratio(pil_image.size, size), Image.ANTIALIAS if antialiasing else Image.NEAREST) image = pygame.image.fromstring(pil_image.tobytes(), pil_image.size, pil_image.mode) if hflip or vflip: image = pygame.transform.flip(image, hflip, vflip) if angle != 0: image = pygame.transform.rotate(image, angle) return image
def _fit_to_resolution(self, image): """Resize and crop to fit the desired resolution. """ # Same process as RPI camera (almost), this is for keeping # final rendering sizing (see pibooth.picutres.maker) image = image.resize( sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer')) return image.crop( sizing.new_size_by_croping(image.size, self.resolution))
def _fit_to_resolution(self, image): """Resize and crop to fit the desired resolution. """ # Same process as RPI camera (almost), this is for keeping # final rendering sizing (see pibooth.picutres.maker) height, width = image.shape[:2] size = sizing.new_size_keep_aspect_ratio((width, height), self.resolution, 'outer') image = cv2.resize(image, size, interpolation=cv2.INTER_AREA) height, width = image.shape[:2] cropped = sizing.new_size_by_croping((width, height), self.resolution) return image[cropped[1]:cropped[3], cropped[0]:cropped[2]]
def _post_process_capture(self, capture_path): gp_path = self._captures[capture_path] camera_file = gp.check_result(gp.gp_camera_file_get( self._cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL)) image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size()))) image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer'), Image.ANTIALIAS) image = image.crop(sizing.new_size_by_croping(image.size, self.resolution)) if self._capture_hflip: image = image.transpose(Image.FLIP_LEFT_RIGHT) image.save(capture_path) return image
def new_image_with_background(width, height, background): """Create a new image with the given background. The background can be a RGB color tuple ora PIL image. """ if isinstance(background, (tuple, list)): return Image.new('RGB', (width, height), color=background) else: image = Image.new('RGB', (width, height)) image.paste( background.resize( sizing.new_size_keep_aspect_ratio(background.size, image.size, 'outer'))) return image
def get_image(name, size=None, antialiasing=True): """Return a Pygame image. If a size is given, the image is resized keeping the original image's aspect ratio. :param antialiasing: use antialiasing algorithm when resize """ if not size: return pygame.image.load(get_filename(name)).convert() else: image = Image.open(get_filename(name)) image = image.resize( sizing.new_size_keep_aspect_ratio(image.size, size), Image.ANTIALIAS if antialiasing else Image.NEAREST) return pygame.image.fromstring(image.tobytes(), image.size, image.mode)
def _update_foreground(self, pil_image, pos=CENTER, resize=True): """Show a PIL image on the foreground. Only one is bufferized to avoid memory leak. """ image_name = id(pil_image) if pos == self.FULLSCREEN: image_size_max = (self.surface.get_size()[0] * 0.9, self.surface.get_size()[1] * 0.9) else: image_size_max = (self.surface.get_size()[0] * 0.48, self.surface.get_size()[1]) buff_size, buff_image = self._buffered_images.get( image_name, (None, None)) if buff_image and image_size_max == buff_size: image = buff_image else: if resize: image = pil_image.resize( sizing.new_size_keep_aspect_ratio(pil_image.size, image_size_max), Image.ANTIALIAS) else: image = pil_image image = pygame.image.frombuffer(image.tobytes(), image.size, image.mode) if self._current_foreground: self._buffered_images.pop(id(self._current_foreground[0]), None) LOGGER.debug("Add to buffer the image '%s'", image_name) self._buffered_images[image_name] = (image_size_max, image) self._current_foreground = (pil_image, pos, resize) if self.debug and resize: # Build rectangle around picture area for debuging purpose outlines = pygame.Surface(image_size_max, pygame.SRCALPHA, 32) pygame.draw.rect(outlines, pygame.Color(255, 0, 0), outlines.get_rect(), 2) self.surface.blit(outlines, self._pos_map[pos](outlines)) return self.surface.blit(image, self._pos_map[pos](image))
def _get_preview_image(self): """Capture a new preview image. """ rect = self.get_rect() if self._preview_compatible: cam_file = self._cam.capture_preview() image = Image.open(io.BytesIO(cam_file.get_data_and_size())) # Crop to keep aspect ratio of the resolution image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution)) # Resize to fit the available space in the window image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, (rect.width, rect.height), 'outer')) if self._preview_hflip: image = image.transpose(Image.FLIP_LEFT_RIGHT) else: image = Image.new('RGB', (rect.width, rect.height), color=(0, 0, 0)) if self._overlay: image.paste(self._overlay, (0, 0), self._overlay) return image
def get_pygame_image(name, size=None, antialiasing=True, hflip=False, vflip=False, crop=False): """Return a Pygame image. If a size is given, the image is resized keeping the original image's aspect ratio. :param name: name of an image located in language folders :type name: str :param size: resize image to this size :type size: tuple :param antialiasing: use antialiasing algorithm when resize :type antialiasing: bool :param hflip: apply an horizontal flip :type hflip: bool :param vflip: apply a vertical flip :type vflip: bool :param crop: crop image to fit aspect ration of the size :type crop: bool :return: pygame.Surface with image :rtype: object """ if not size: image = pygame.image.load(get_filename(name)).convert() else: image = Image.open(get_filename(name)) if crop: image = image.crop( sizing.new_size_by_croping_ratio(image.size, size)) image = image.resize( sizing.new_size_keep_aspect_ratio(image.size, size), Image.ANTIALIAS if antialiasing else Image.NEAREST) image = pygame.image.fromstring(image.tobytes(), image.size, image.mode) if hflip or vflip: image = pygame.transform.flip(image, hflip, vflip) return image
def _post_process_capture(self, capture_path): """Rework and return a Image object from file. """ image, effect = self._captures[capture_path] image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Crop to keep aspect ratio of the resolution height, width = image.shape[:2] cropped = sizing.new_size_by_croping_ratio((width, height), self.resolution) image = image[cropped[1]:cropped[3], cropped[0]:cropped[2]] # Resize to fit the resolution height, width = image.shape[:2] size = sizing.new_size_keep_aspect_ratio((width, height), self.resolution, 'outer') image = cv2.resize(image, size, interpolation=cv2.INTER_AREA) if self._capture_hflip: image = cv2.flip(image, 1) if effect != 'none': pass # To be implemented cv2.imwrite(capture_path, cv2.cvtColor(image, cv2.COLOR_RGB2BGR)) return Image.fromarray(image)
def _post_process_capture(self, capture_path): """Rework and return a Image object from file. """ gp_path, effect = self._captures[capture_path] camera_file = self._cam.file_get(gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL) if self.delete_internal_memory: LOGGER.debug("Delete capture '%s' from internal memory", gp_path.name) self._cam.file_delete(gp_path.folder, gp_path.name) image = Image.open(io.BytesIO(camera_file.get_data_and_size())) # Crop to keep aspect ratio of the resolution image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution)) # Resize to fit the resolution image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer')) if self._capture_hflip: image = image.transpose(Image.FLIP_LEFT_RIGHT) if effect != 'none': image = image.filter(getattr(ImageFilter, effect.upper())) image.save(capture_path) return image
def test_resize_original_small_outer(): size = sizing.new_size_keep_aspect_ratio((600, 200), (900, 600), 'outer') assert size == (1800, 600)
def concatenate_pictures_portrait(pictures, footer_texts, bg_color, text_color, inter_width=None): """ Merge up to 4 PIL images in portrait orientation. +---------+ +---------+ +---+-+---+ +---------+ | | | +-+ | | |1| | | +-+ +-+ | | | | |1| | | +-+ | | |1| |2| | | +-+ | | +-+ | | +-+ | | +-+ +-+ | | |1| | | | | |2| | | | | +-+ | | +-+ | | +-+ | | +-+ +-+ | | | | |2| | | +-+ | | |3| |4| | | | | +-+ | | |3| | | +-+ +-+ | +---------+ +---------+ +---+-+---+ +---------+ """ widths, heights = zip(*(i.size for i in pictures)) # starting here we consider that all the images have the same height and widths if inter_width is None: inter_width = max(heights) // 20 if len(pictures) == 1: new_width = max(widths) + inter_width * 2 new_height = max(heights) + inter_width * 2 elif len(pictures) == 2: new_width = max(widths) + inter_width * 2 new_height = max(heights) * 2 + inter_width * 3 elif len(pictures) == 3: new_width = max(widths) + inter_width * 2 new_height = max(heights) * 3 + inter_width * 4 elif len(pictures) == 4: new_width = max(widths) * 2 + inter_width * 3 new_height = max(heights) * 2 + inter_width * 3 else: raise ValueError("List of max 4 pictures expected, got {}".format( len(pictures))) matrix = Image.new('RGBA', (new_width, new_height)) x_offset = inter_width y_offset = inter_width # Consider that the photo are correctly ordered matrix.paste(pictures[0], (x_offset, y_offset)) if len(pictures) == 2: y_offset += (pictures[0].size[1] + inter_width) matrix.paste(pictures[1], (x_offset, y_offset)) elif len(pictures) == 3: y_offset += (pictures[0].size[1] + inter_width) matrix.paste(pictures[1], (x_offset, y_offset)) y_offset += (pictures[1].size[1] + inter_width) matrix.paste(pictures[2], (x_offset, y_offset)) elif len(pictures) == 4: x_offset += (pictures[0].size[0] + inter_width) matrix.paste(pictures[1], (x_offset, y_offset)) y_offset += (pictures[1].size[1] + inter_width) x_offset = inter_width matrix.paste(pictures[2], (x_offset, y_offset)) x_offset += (pictures[2].size[0] + inter_width) matrix.paste(pictures[3], (x_offset, y_offset)) final_width, final_height = 2400, 3600 if not footer_texts[0] and not footer_texts[1]: matrix_width, matrix_height = final_width, final_height footer_size = 0 else: matrix_width, matrix_height = 2400, 3000 footer_size = 600 matrix = matrix.resize( sizing.new_size_keep_aspect_ratio(matrix.size, (matrix_width, matrix_height)), Image.ANTIALIAS) final_image = new_image_with_background(final_width, final_height, bg_color) final_image.paste(matrix, ((final_width - matrix.size[0]) // 2, (final_height - footer_size - matrix.size[1]) // 2), mask=matrix) if footer_size: # Text part draw = ImageDraw.Draw(final_image) # Footer 1 name_font = ImageFont.truetype(fonts.get_filename("Amatic-Bold.ttf"), int(2 / 3. * footer_size)) name_width, name_height = draw.textsize(footer_texts[0], font=name_font) footer_x = (final_width - name_width) // 2 footer_y = final_height - footer_size - 100 draw.text((footer_x, footer_y), footer_texts[0], text_color, font=name_font) # Footer 2 date_font = ImageFont.truetype( fonts.get_filename("AmaticSC-Regular.ttf"), int(1 / 3. * footer_size)) date_width, date_height = draw.textsize(footer_texts[1], font=date_font) footer_x = (final_width - date_width) // 2 footer_y = final_height - footer_size + 300 draw.text((footer_x, footer_y), footer_texts[1], text_color, font=date_font) return final_image
def test_resize_original_big_inner(): size = sizing.new_size_keep_aspect_ratio((1000, 1200), (800, 600)) assert size == (500, 600)
def test_resize_original_big_outer(): size = sizing.new_size_keep_aspect_ratio((1000, 1200), (800, 600), 'outer') assert size == (800, 960)
def test_resize_original_small_inner(): size = sizing.new_size_keep_aspect_ratio((600, 200), (900, 600)) assert size == (900, 300)