| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | """
 | 
					
						
							|  |  |  | Custom image class for Inkycal Project | 
					
						
							|  |  |  | Takes care of handling images. Made to be used by other modules to handle | 
					
						
							|  |  |  | images. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-03 16:16:07 +02:00
										 |  |  | Copyright by aceinnolab | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | """
 | 
					
						
							|  |  |  | import logging | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  | from typing import Literal | 
					
						
							| 
									
										
										
										
											2023-12-14 01:51:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  | import numpy | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  | import PIL | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  | import requests | 
					
						
							|  |  |  | from PIL import Image | 
					
						
							| 
									
										
										
										
											2022-04-14 05:53:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 02:56:04 +02:00
										 |  |  | logger = logging.getLogger(__name__) | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | class Inkyimage: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     """Custom Imgae class written for commonly used image operations.""" | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |     def __init__(self, image=None): | 
					
						
							| 
									
										
										
										
											2023-12-07 18:40:24 +01:00
										 |  |  |         """Initialize InkyImage module""" | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         # no image initially | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  |         self.image = image | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         # give an OK message | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         logger.info(f"{__name__} loaded") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     def load(self, path: str) -> None: | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         """loads an image from a URL or filepath.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |           - path:The full path or url of the image file | 
					
						
							|  |  |  |             e.g. `https://sample.com/logo.png` or `/home/pi/Downloads/nice_pic.png` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Raises: | 
					
						
							|  |  |  |           - FileNotFoundError: This Exception is raised when the file could not be | 
					
						
							|  |  |  |             found. | 
					
						
							|  |  |  |           - OSError: A OSError is raised when the URL doesn't point to the correct | 
					
						
							|  |  |  |             file-format, i.e. is not an image | 
					
						
							|  |  |  |           - TypeError: if the URLS doesn't start with htpp | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # Try to open the image if it exists and is an image file | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             if path.startswith("http"): | 
					
						
							|  |  |  |                 logger.info("loading image from URL") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 image = Image.open(requests.get(path, stream=True).raw) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.info("loading image from local path") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 image = Image.open(path) | 
					
						
							|  |  |  |         except FileNotFoundError: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             logger.error("No image file found", exc_info=True) | 
					
						
							|  |  |  |             raise Exception(f"Your file could not be found. Please check the filepath: {path}") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         except OSError: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             logger.error("Invalid Image file provided", exc_info=True) | 
					
						
							|  |  |  |             raise Exception("Please check if the path points to an image file.") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         logger.info(f"width: {image.width}, height: {image.height}") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         image.convert(mode="RGBA")  # convert to a more suitable format | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  |         self.image = image | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         logger.info("loaded Image") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def clear(self): | 
					
						
							|  |  |  |         """Removes currently saved image if present.""" | 
					
						
							|  |  |  |         if self.image: | 
					
						
							|  |  |  |             self.image = None | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             logger.info("cleared previous image") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _preview(self): | 
					
						
							|  |  |  |         """Preview the image on gpicview (only works on Rapsbian with Desktop)""" | 
					
						
							|  |  |  |         if self._image_loaded(): | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             path = "/home/pi/Desktop/" | 
					
						
							|  |  |  |             self.image.save(path + "temp.png") | 
					
						
							|  |  |  |             os.system("gpicview " + path + "temp.png") | 
					
						
							|  |  |  |             os.system("rm " + path + "temp.png") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def preview(image): | 
					
						
							| 
									
										
										
										
											2023-11-21 15:18:19 +01:00
										 |  |  |         """Previews an image on gpicview (only works on Rapsbian with Desktop).""" | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         path = "~/temp" | 
					
						
							|  |  |  |         image.save(path + "/temp.png") | 
					
						
							|  |  |  |         os.system("gpicview " + path + "/temp.png") | 
					
						
							|  |  |  |         os.system("rm " + path + "/temp.png") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _image_loaded(self): | 
					
						
							|  |  |  |         """returns True if image was loaded""" | 
					
						
							|  |  |  |         if self.image: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             logger.error("image not loaded") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def flip(self, angle): | 
					
						
							|  |  |  |         """Flips the image by the given angle.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |           - angle:->int. A multiple of 90, e.g. 90, 180, 270, 360. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._image_loaded(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             image = self.image | 
					
						
							|  |  |  |             if not angle % 90 == 0: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.error("Angle must be a multiple of 90") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             image = image.rotate(angle, expand=True) | 
					
						
							|  |  |  |             self.image = image | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             logger.info(f"flipped image by {angle} degrees") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:46:33 +01:00
										 |  |  |     def autoflip(self, layout: str) -> None: | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         """flips the image automatically to the given layout.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |           - layout:-> str. Choose `horizontal` or `vertical`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Checks the image's width and height. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         In horizontal mode, the image is flipped if the image height is greater | 
					
						
							|  |  |  |         than the image width. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         In vertical mode, the image is flipped if the image width is greater | 
					
						
							|  |  |  |         than the image height. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._image_loaded(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             image = self.image | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             if layout == "horizontal": | 
					
						
							| 
									
										
										
										
											2022-04-10 06:35:08 +02:00
										 |  |  |                 if image.height > image.width: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                     logger.info("image width greater than image height, flipping") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                     image = image.rotate(90, expand=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             elif layout == "vertical": | 
					
						
							| 
									
										
										
										
											2022-04-10 06:35:08 +02:00
										 |  |  |                 if image.width > image.height: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                     logger.info("image width greater than image height, flipping") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                     image = image.rotate(90, expand=True) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.error("layout not supported") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 return | 
					
						
							|  |  |  |             self.image = image | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_alpha(self): | 
					
						
							|  |  |  |         """Removes transparency if image has transparency.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Checks if an image has an alpha band and replaces the transparency with | 
					
						
							|  |  |  |         white pixels. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._image_loaded(): | 
					
						
							|  |  |  |             image = self.image | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if len(image.getbands()) == 4: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.info("removing alpha channel") | 
					
						
							|  |  |  |                 bg = Image.new("RGBA", (image.width, image.height), "white") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 im = Image.alpha_composite(bg, image) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 self.image.paste(im, (0, 0)) | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.info("removed transparency") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def resize(self, width=None, height=None): | 
					
						
							|  |  |  |         """Resize an image to desired width or height""" | 
					
						
							|  |  |  |         if self._image_loaded(): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  |             if not width and not height: | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 logger.error("no height of width specified") | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             image = self.image | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if width: | 
					
						
							|  |  |  |                 initial_width = image.width | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 wpercent = width / float(image.width) | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 hsize = int((float(image.height) * float(wpercent))) | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  |                 image = image.resize((width, hsize), Image.LANCZOS) | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 logger.info(f"resized image from {initial_width} to {image.width}") | 
					
						
							|  |  |  |                 self.image = image | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if height: | 
					
						
							|  |  |  |                 initial_height = image.height | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |                 hpercent = height / float(image.height) | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 wsize = int(float(image.width) * float(hpercent)) | 
					
						
							| 
									
										
										
										
											2022-10-02 00:49:27 +02:00
										 |  |  |                 image = image.resize((wsize, height), Image.LANCZOS) | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |                 logger.info(f"resized image from {initial_height} to {image.height}") | 
					
						
							|  |  |  |                 self.image = image | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def merge(image1, image2): | 
					
						
							|  |  |  |         """Merges two images into one.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Replaces white pixels of the first image with transparent ones. Then pastes | 
					
						
							|  |  |  |         the first image on the second one. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |           - image1: A PIL Image object in 'RGBA' mode. | 
					
						
							|  |  |  |           - image2: A PIL Image object in 'RGBA' mode. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns: | 
					
						
							|  |  |  |           - A single image. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def clear_white(img): | 
					
						
							|  |  |  |             """Replace all white pixels from image with transparent pixels""" | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |             x = numpy.asarray(img.convert("RGBA")).copy() | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |             x[:, :, 3] = (255 * (x[:, :, :3] != 255).any(axis=2)).astype(numpy.uint8) | 
					
						
							|  |  |  |             return Image.fromarray(x) | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         image2 = clear_white(image2) | 
					
						
							|  |  |  |         image1.paste(image2, (0, 0), image2) | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         logger.info("merged given images into one") | 
					
						
							| 
									
										
										
										
											2020-12-05 00:17:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-02 01:30:17 +02:00
										 |  |  |         return image1 | 
					
						
							| 
									
										
										
										
											2020-12-05 00:17:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  | def image_to_palette( | 
					
						
							|  |  |  |     image: Image, palette: Literal = ["bwr", "bwy", "bw", "16gray"], dither: bool = True | 
					
						
							|  |  |  | ) -> (PIL.Image, PIL.Image): | 
					
						
							|  |  |  |     """Maps an image to a given colour palette.
 | 
					
						
							| 
									
										
										
										
											2020-12-05 00:17:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     Maps each pixel from the image to a colour from the palette. | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     Args: | 
					
						
							|  |  |  |         - palette: A supported token. (see below) | 
					
						
							|  |  |  |         - dither:->bool. Use dithering? Set to `False` for solid colour fills. | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     Returns: | 
					
						
							|  |  |  |         - two images: one for the coloured band and one for the black band. | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     Raises: | 
					
						
							|  |  |  |         - ValueError if palette token is not supported | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     Supported palette tokens: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     >>> 'bwr' # black-white-red | 
					
						
							|  |  |  |     >>> 'bwy' # black-white-yellow | 
					
						
							|  |  |  |     >>> 'bw'  # black-white | 
					
						
							|  |  |  |     >>> '16gray' # 16 shades of gray | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     if palette == "bwr": | 
					
						
							|  |  |  |         # black-white-red palette | 
					
						
							|  |  |  |         pal = [255, 255, 255, 0, 0, 0, 255, 0, 0] | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     elif palette == "bwy": | 
					
						
							|  |  |  |         # black-white-yellow palette | 
					
						
							|  |  |  |         pal = [255, 255, 255, 0, 0, 0, 255, 255, 0] | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     elif palette == "bw": | 
					
						
							|  |  |  |         pal = None | 
					
						
							|  |  |  |     elif palette == "16gray": | 
					
						
							|  |  |  |         pal = [x for x in range(0, 256, 16)] * 3 | 
					
						
							|  |  |  |         pal.sort() | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         logger.error("The given palette is unsupported.") | 
					
						
							| 
									
										
										
										
											2024-02-10 22:43:57 +01:00
										 |  |  |         raise ValueError(f"The given palette ({palette}) is not supported.") | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     if pal: | 
					
						
							|  |  |  |         # The palette needs to have 256 colors, for this, the black-colour | 
					
						
							|  |  |  |         # is added until the | 
					
						
							|  |  |  |         colours = len(pal) // 3 | 
					
						
							|  |  |  |         # print(f'The palette has {colours} colours') | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         if 256 % colours != 0: | 
					
						
							|  |  |  |             # print('Filling palette with black') | 
					
						
							|  |  |  |             pal += (256 % colours) * [0, 0, 0] | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # print(pal) | 
					
						
							|  |  |  |         colours = len(pal) // 3 | 
					
						
							|  |  |  |         # print(f'The palette now has {colours} colours') | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Create a dummy image to be used as a palette | 
					
						
							|  |  |  |         palette_im = Image.new("P", (1, 1)) | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Attach the created palette. The palette should have 256 colours | 
					
						
							|  |  |  |         # equivalent to 768 integers | 
					
						
							|  |  |  |         palette_im.putpalette(pal * (256 // colours)) | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Quantize the image to given palette | 
					
						
							|  |  |  |         quantized_im = image.quantize(palette=palette_im, dither=dither) | 
					
						
							|  |  |  |         quantized_im = quantized_im.convert("RGB") | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # get rgb of the non-black-white colour from the palette | 
					
						
							|  |  |  |         rgb = [pal[x : x + 3] for x in range(0, len(pal), 3)] | 
					
						
							|  |  |  |         rgb = [col for col in rgb if col != [0, 0, 0] and col != [255, 255, 255]][0] | 
					
						
							|  |  |  |         r_col, g_col, b_col = rgb | 
					
						
							|  |  |  |         # print(f'r:{r_col} g:{g_col} b:{b_col}') | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Create an image buffer for black pixels | 
					
						
							|  |  |  |         buffer1 = numpy.array(quantized_im) | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Get RGB values of each pixel | 
					
						
							|  |  |  |         r, g, b = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # convert coloured pixels to white | 
					
						
							|  |  |  |         buffer1[numpy.logical_and(r == r_col, g == g_col)] = [255, 255, 255] | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # reconstruct image for black-band | 
					
						
							|  |  |  |         im_black = Image.fromarray(buffer1) | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Create a buffer for coloured pixels | 
					
						
							|  |  |  |         buffer2 = numpy.array(quantized_im) | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # Get RGB values of each pixel | 
					
						
							|  |  |  |         r, g, b = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # convert black pixels to white | 
					
						
							|  |  |  |         buffer2[numpy.logical_and(r == 0, g == 0)] = [255, 255, 255] | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # convert non-white pixels to black | 
					
						
							|  |  |  |         buffer2[numpy.logical_and(g == g_col, b == 0)] = [0, 0, 0] | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # reconstruct image for colour-band | 
					
						
							|  |  |  |         im_colour = Image.fromarray(buffer2) | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |         # self.preview(im_black) | 
					
						
							|  |  |  |         # self.preview(im_colour) | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         im_black = image.convert("1", dither=dither) | 
					
						
							|  |  |  |         im_colour = Image.new(mode="1", size=im_black.size, color="white") | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     logger.info("mapped image to specified palette") | 
					
						
							| 
									
										
										
										
											2020-12-02 00:57:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  |     return im_black, im_colour | 
					
						
							| 
									
										
										
										
											2020-11-29 14:54:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-20 17:15:03 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     print(f"running {__name__} in standalone/debug mode") |