Added custom image class
This commit is contained in:
		
							
								
								
									
										246
									
								
								inkycal/modules/inky_image.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								inkycal/modules/inky_image.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| #!/usr/bin/python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| Custom image class for Inkycal Project | ||||
| Takes care of handling images. Made to be used by other modules to handle | ||||
| images. | ||||
|  | ||||
| Copyright by aceisace | ||||
| """ | ||||
|  | ||||
| from PIL import Image, ImageOps | ||||
| import requests | ||||
| import numpy | ||||
| import os | ||||
| import logging | ||||
|  | ||||
| filename = os.path.basename(__file__).split('.py')[0] | ||||
| logger = logging.getLogger(filename) | ||||
|  | ||||
| class Inkyimage: | ||||
|   """Inkyimage class | ||||
|  | ||||
|   missing documentation, lazy devs :/ | ||||
|   """ | ||||
|  | ||||
|   def __init__(self, image=None): | ||||
|     """Initialize Inkyimage module""" | ||||
|  | ||||
|     # no image initially | ||||
|     self.image = image | ||||
|  | ||||
|     # give an OK message | ||||
|     print(f'{filename} loaded') | ||||
|  | ||||
|   def load(self, path): | ||||
|     """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: | ||||
|       if path.startswith('http'): | ||||
|         logger.debug('loading image from URL') | ||||
|         image = Image.open(requests.get(path, stream=True).raw) | ||||
|       else: | ||||
|         logger.info('loading image from local path') | ||||
|         image = Image.open(path) | ||||
|     except FileNotFoundError: | ||||
|       raise ('Your file could not be found. Please check the filepath') | ||||
|     except OSError: | ||||
|       raise ('Please check if the path points to an image file.') | ||||
|  | ||||
|     logger.debug(f'width: {image.width}, height: {image.height}') | ||||
|  | ||||
|     image.convert(mode='RGBA') #convert to a more suitable format | ||||
|     self.image = image | ||||
|     print('loaded Image') | ||||
|  | ||||
|   def clear(self): | ||||
|     """Removes currently saved image if present""" | ||||
|     if self.image: | ||||
|       self.image = None | ||||
|       print('cleared') | ||||
|  | ||||
|   def _preview(self): | ||||
|     """Preview the image on gpicview (only works on Rapsbian with Desktop)""" | ||||
|     if self._image_loaded(): | ||||
|       path = '/home/pi/Desktop/' | ||||
|       self.image.save(path+'temp.png') | ||||
|       os.system("gpicview "+path+'temp.png') | ||||
|       os.system('rm '+path+'temp.png') | ||||
|  | ||||
|   @staticmethod | ||||
|   def preview(image): | ||||
|     """"Previews an image on gpicview (only works on Rapsbian with Desktop) | ||||
|  | ||||
|  | ||||
|     """ | ||||
|     path = '/home/pi/Desktop/' | ||||
|     image.save(path+'temp.png') | ||||
|     os.system("gpicview "+path+'temp.png') | ||||
|     os.system('rm '+path+'temp.png') | ||||
|  | ||||
|   def _image_loaded(self): | ||||
|     """returns True if image was loaded""" | ||||
|     if self.image: | ||||
|       return True | ||||
|     else: | ||||
|       print('image not loaded') | ||||
|       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: | ||||
|         print('Angle must be a multiple of 90') | ||||
|         return | ||||
|  | ||||
|       image = image.rotate(angle, expand = True) | ||||
|       self.image = image | ||||
|       print(f'flipped image by {angle} degrees') | ||||
|  | ||||
|   def autoflip(self, layout): | ||||
|     """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 | ||||
|       if layout == 'horizontal': | ||||
|         if (image.height > image.width): | ||||
|           print('image width greater than image height, flipping') | ||||
|           image = image.rotate(90, expand=True) | ||||
|  | ||||
|       elif layout == 'vertical': | ||||
|         if (image.width > image.height): | ||||
|           print('image width greater than image height, flipping') | ||||
|           image = image.rotate(90, expand=True) | ||||
|       else: | ||||
|         print('layout not supported') | ||||
|         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: | ||||
|         print('has alpha') | ||||
|         logger.debug('removing transparency') | ||||
|         bg = Image.new('RGBA', (image.width, image.height), 'white') | ||||
|         im = Image.alpha_composite(bg, image) | ||||
|  | ||||
|         self.image.paste(im, (0,0)) | ||||
|         print('removed alpha') | ||||
|  | ||||
|   def resize(self, width=None, height=None): | ||||
|     """Resize an image to desired width or height""" | ||||
|     if self._image_loaded(): | ||||
|        | ||||
|       if width == None and height == None: | ||||
|         print('no height of width specified') | ||||
|         return | ||||
|  | ||||
|       image = self.image | ||||
|  | ||||
|       if width: | ||||
|         initial_width = image.width | ||||
|         wpercent = (width/float(image.width)) | ||||
|         hsize = int((float(image.height)*float(wpercent))) | ||||
|         image = image.resize((width, hsize), Image.ANTIALIAS) | ||||
|         logger.debug(f"resized image from {initial_width} to {image.width}") | ||||
|         self.image = image | ||||
|  | ||||
|       if height: | ||||
|         initial_height = image.height | ||||
|         hpercent = (height / float(image.height)) | ||||
|         wsize = int(float(image.width) * float(hpercent)) | ||||
|         image = image.resize((wsize, height), Image.ANTIALIAS) | ||||
|         logger.debug(f"resized image from {initial_height} to {image.height}") | ||||
|         self.image = image | ||||
|  | ||||
|   def to_mono(self): | ||||
|     """Converts image to pure balck-white image (1-bit). | ||||
|  | ||||
|     retrns 1-bit image | ||||
|  | ||||
|     """ | ||||
|     if self._image_loaded(): | ||||
|       image = self.image | ||||
|  | ||||
|       image = image.convert('1', dither=True) | ||||
|       return image | ||||
|  | ||||
|  | ||||
|   def to_colour(self): | ||||
|     """Maps image colours to 3 colours. | ||||
|     """ | ||||
|     if self._image_loaded(): | ||||
|       image = self.image.convert('RGB') | ||||
|  | ||||
|       # Create a simple palette | ||||
|       pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] | ||||
|  | ||||
|       # Map each pixel of the opened image to the Palette | ||||
|       palette_im = Image.new('P', (3,1)) | ||||
|       palette_im.putpalette(pal * 64) | ||||
|       quantized_im = image.quantize(palette=palette_im) | ||||
|       quantized_im.convert('RGB') | ||||
|  | ||||
|       # Create a buffer for coloured pixels | ||||
|       buffer1 = numpy.array(quantized_im.convert('RGB')) | ||||
|       r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] | ||||
|  | ||||
|       # Create a buffer for black pixels | ||||
|       buffer2 = numpy.array(quantized_im.convert('RGB')) | ||||
|       r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] | ||||
|  | ||||
|       # re-construct image from coloured-pixels buffer | ||||
|       buffer2[numpy.logical_and(r2 ==  0, b2 == 0)] = [255,255,255] # black->white | ||||
|       buffer2[numpy.logical_and(r2 ==  255, b2 == 0)] = [0,0,0] #red->black | ||||
|       im_colour = Image.fromarray(buffer2) | ||||
|  | ||||
|       # re-construct image from black pixels buffer | ||||
|       buffer1[numpy.logical_and(r1 ==  255, b1 == 0)] = [255,255,255] | ||||
|       im_black = Image.fromarray(buffer1) | ||||
|  | ||||
|       return im_black, im_colour | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|   print(f'running {filename} in standalone/debug mode') | ||||
|  | ||||
| a = Inkyimage() | ||||
| a.load('https://pngimg.com/uploads/pokemon/pokemon_PNG148.png') | ||||
		Reference in New Issue
	
	Block a user