def open_img_file( img_file: Path, crop_ratio: Optional[float] = None, size: Optional[Tuple[int, int]] = None, convert: Optional[str] = None, ) -> Image.Image: """Open an image file with some extra bells and whistles. Args: img_file: path to the image. crop_ratio: width to height to which to crop the image. size: resize image. convert: convert the image to the provided mode. See PIL image modes. Returns: Image instance. """ with Image.open(img_file) as img: img = exif_transpose(img) if crop_ratio is not None: img = crop_to_ratio(img, crop_ratio) if size is not None: img = img.resize(size) if convert is not None: img = img.convert(convert) else: img = img.convert("RGB") return img
def open_img(fp): pil_img = Image.open(fp) try: pil_img = exif_transpose(pil_img) except: pass return np.array(pil_img)
def get_orientated_image(source, default_flip: bool = True) -> Image: """ Load and rotate/transpose image according to EXIF orientation, if any. If missing orientation and the image was fetched from iNat, it will be vertically mirrored. (?) """ image = Image.open(source) exif = image.getexif() if exif.get(EXIF_ORIENTATION_ID): image = exif_transpose(image) # TODO: In the future there may be more cases than just local images and remote images from iNat elif default_flip and isinstance(source, IOBase): image = flip(image) return image
def thumbnail( self, ns_path: StrOrPath, path: StrOrPath, size: int, ) -> tuple[int, IO[bytes]]: fullpath = self._joinpath(self.location, ns_path, path) buffer = BytesIO() try: with Image.open(fullpath) as im: im.thumbnail((size, size)) exif_transpose(im).save(buffer, im.format) except FileNotFoundError as exc: raise errors.FileNotFound() from exc except IsADirectoryError as exc: raise errors.IsADirectory(f"Path '{path}' is a directory") from exc except UnidentifiedImageError as exc: msg = f"Can't generate thumbnail for a file: '{path}'" raise errors.ThumbnailUnavailable(msg) from exc size = buffer.seek(0, 2) buffer.seek(0) return size, buffer
async def thumbnail(self, data: bytes | str, content_type: str = '') -> Resource.Thumbnail: """Generate a thumbnail from image *data*. *content_type* is the media type of the image. The generated thumbnail is stored in :attr:`files`. If *data* is corrupt, a :exc:`BrokenResourceError` is raised. If alternatively an image *url* is given, the image is fetched first. If there is a problem, an :exc:`AnalysisError` or :exc:`CommunicationError` is raised. """ if isinstance(data, str): data, content_type, _ = await self.fetch(data) return await self.thumbnail(data, content_type) if not self.files: raise ValueError('No files') if content_type in { 'image/bmp', 'image/gif', 'image/jpeg', 'image/png' }: try: with PIL.Image.open(BytesIO(data), formats=[content_type[6:]]) as src: image = exif_transpose(src) image.thumbnail(Analyzer.THUMBNAIL_SIZE) if image.mode == 'RGB': r, g, b = cast(tuple[float, float, float], Stat(image).mean) color = f'#{int(r):02x}{int(g):02x}{int(b):02x}' else: # At the moment, transparent (RGBA, LA), grayscale (L, I, 1), CMYK and color # palette (P) images are not handled color = '#ffffff' stream = BytesIO() image.save(stream, format=cast(str, src.format)) data = stream.getvalue() except DecompressionBombError as e: raise BrokenResourceError('Exceeding data size') from e except OSError as e: raise BrokenResourceError('Bad data') from e elif content_type == 'image/svg+xml': color = '#ffffff' else: raise BrokenResourceError(f'Unknown content_type {content_type}') url = await self.files.write(data, content_type) return Resource.Thumbnail(url, color)
def pil_loader(path): with open(path, "rb") as f: image = Image.open(f) exif_data = None try: exif_data = image._getexif() except: pass if exif_data: orientation = exif_data.get(INV_TAGS["Orientation"]) if orientation: image = exif_transpose(image) # If RGB images are uniformly desired, # instead of inhomogeneous RGBA, gray-scale images, # then uncomment the next line image = image.convert("RGB") return image
def create_thumbnail(self, image_path: str) -> str: """Create a thumbnail for `image` and return its file path. If the thumbnail already exists, just return its path. If the thumbnail cannot be created, return the path to the stock image 'missing_image.png'. Parameters ---------- image_path Path of the image, relative to `self.root`. Returns ------- Absolute file path of the thumbnail. """ thumbnail_path = os.path.join(self.thumbnail_dir, image_path) # https://openclipart.org/detail/298746/missing-image try: if os.path.exists(thumbnail_path): return thumbnail_path with PILImage.open(os.path.join(self.root, image_path)) as image: thumbnail_dir = os.path.dirname(thumbnail_path) if not os.path.exists(thumbnail_dir): os.makedirs(thumbnail_dir) # Transpose the image, because `thumbnail()` doesn't retain # exif-data and thus removes the "Orientation". transposed_image = exif_transpose(image) transposed_image.thumbnail((100, 100)) transposed_image.save(thumbnail_path) except OSError: thumbnail_path = "resources/missing_image.png" return thumbnail_path
def pil_open_img(image: Image) -> PIL_Image: ''' Opens image model in PIL ''' pil: PIL_Image = exif_transpose(PIL.Image.open(image.filepath)) return pil