diff --git a/modules/inkycal_image.py b/modules/inkycal_image.py index 209afb0..0840347 100644 --- a/modules/inkycal_image.py +++ b/modules/inkycal_image.py @@ -2,82 +2,166 @@ # -*- coding: utf-8 -*- """ Experimental image module for Inky-Calendar software -Displays an image on the E-Paper. Currently only supports black and white +Displays an image on the E-Paper. Work in progress! Copyright by aceisace """ from __future__ import print_function -from PIL import Image from configuration import * -import os +from os import path +from PIL import ImageOps +import requests +import numpy -import inkycal_drivers as drivers +"""----------------------------------------------------------------""" +#path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' +path ='/home/pi/Inky-Calendar/images/canvas.png' +mode = 'auto' # 'horizontal' # 'vertical' # 'auto' +upside_down = True # Flip image by 180 deg (upside-down) +alignment = 'center' # top_center, top_left, center_left, bottom_right etc. +colours = 'bwr' # bwr # bwy # bw +render = True # show image on E-Paper? +"""----------------------------------------------------------------""" -display = drivers.EPD() +"""Try to open the image if it exists and is an image file""" +try: + if 'http' in path: + im = Image.open(requests.get(path, stream=True).raw) + else: + im = Image.open(path) +except FileNotFoundError: + print('Your file could not be found. Please check the path to your file.') + raise +except OSError: + print('Please check if the path points to an image file.') + raise -# Where is the image? -path = '/home/pi//Desktop/test.JPG' +"""Turn image upside-down if specified""" +if upside_down == True: + im.rotate(180, expand = True) -class inkycal_image: +if mode == 'horizontal': + display_width, display_height == display_height, display_width - def __init__(self, path): - self.image = Image.open(path) - self.im_width = self.image.width - self.im_height = self.image.height +if mode == 'vertical': + pass - def check_mode(self): - if self.image.mode != 'RGB' or 'L' or '1': - print('Image mode not supported, converting') - self.image = self.image.convert('RGB') +if mode == 'auto': + if (im.width > im.height) and (display_width < display_height): + print('display vertical, image horizontal -> flipping image') + im = im.rotate(90, expand=True) + if (im.width < im.height) and (display_width > display_height): + print('display horizontal, image vertical -> flipping image') + im = im.rotate(90, expand=True) - def preview(self): - self.image.save(path+'temp.png') - os.system("gpicview "+path+'temp.png') - os.system('rm '+path+'temp.png') - +def fit_width(image, width): + """Resize an image to desired width""" + print('resizing width from', image.width, 'to', end = ' ') + wpercent = (display_width/float(image.width)) + hsize = int((float(image.height)*float(wpercent))) + img = image.resize((width, hsize), Image.ANTIALIAS) + print(img.width) + return img - def check_size(self, alignment = 'middle', padding_colour='white'): - if display_height < self.im_height or display_width < self.im_width: - print('Image too large for the display, cropping image') - if alignment == 'middle' or None: - x1 = int((self.im_width - display_width) / 2) - y1 = int((self.im_height - display_height) / 2) - x2,y2 = x1+display_width, y1+display_height - self.image = self.image.crop((x1,y1,x2,y2)) - - if alignment != 'middle' or None: - print('Sorry, this feature has not been implemented yet') - raise NotImplementedError +def fit_height(image, height): + """Resize an image to desired height""" + print('resizing height from', image.height, 'to', end = ' ') + hpercent = (height / float(image.height)) + wsize = int(float(image.width) * float(hpercent)) + img = image.resize((wsize, height), Image.ANTIALIAS) + print(img.height) + return img - elif display_height > self.im_height and display_width > self.im_width: - print('Image smaller than display, shifting image to center') - x = int( (display_width - self.im_width) /2) - y = int( (display_height - self.im_height) /2) - canvas = Image.new('RGB', (display_width, display_height), color=padding_colour) - canvas.paste(self.image, (x,y)) - self.image = canvas +if im.width > display_width: + im = fit_width(im, display_width) +if im.height > display_height: + im = fit_height(im, display_height) - else: - print('Image file exact. no further action required') +if alignment == 'center': + x,y = int((display_width-im.width)/2), int((display_height-im.height)/2) +elif alignment == 'center_right': + x, y = display_width-im.width, int((display_height-im.height)/2) +elif alignment == 'center_left': + x, y = 0, int((display_height-im.height)/2) - def auto_flip(self): - if self.im_height < self.im_width: - print('rotating image') - self.image = self.image.rotate(270, expand=True) - self.im_width = self.image.width - self.im_height = self.image.height - - - def to_mono(self): - self.image = self.image.convert('1', dither=True) +elif alignment == 'top_center': + x, y = int((display_width-im.width)/2), 0 +elif alignment == 'top_right': + x, y = display_width-im.width, 0 +elif alignment == 'top_left': + x, y = 0, 0 - def prepare_image(self, alignment='middle'): - self.check_mode() - self.auto_flip() - self.check_size(alignment = alignment) - self.to_mono() +elif alignment == 'bottom_center': + x, y = int((display_width-im.width)/2), display_height-im.height +elif alignment == 'bottom_right': + x, y = display_width-im.width, display_height-im.height +elif alignment == 'bottom_left': + x, y = display_width-im.width, display_height-im.height - return self.image +if len(im.getbands()) == 4: + print('removing transparency') + bg = Image.new('RGBA', (im.width, im.height), 'white') + im = Image.alpha_composite(bg, im) -#single line command: -display.show_image(inkycal_image(path).prepare_image(), reduce_colours=False) - +image.paste(im, (x,y)) +im = image + +if colours == 'bw': + """For black-white images, use monochrome dithering""" + black = im.convert('1', dither=True) +elif colours == 'bwr': + """For black-white-red images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] +elif colours == 'bwy': + """For black-white-yellow images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,255,0, 255,255,255] + + +"""Map each pixel of the opened image to the Palette""" +if colours != 'bw': + palette_im = Image.new('P', (3,1)) + palette_im.putpalette(pal * 64) + quantized_im = im.quantize(palette=palette_im) + quantized_im.convert('RGB') + + """Create buffer for coloured pixels""" + buffer1 = numpy.array(quantized_im.convert('RGB')) + r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] + + """Create buffer for black pixels""" + buffer2 = numpy.array(quantized_im.convert('RGB')) + r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] + + if colours == 'bwr': + """Create image for only red pixels""" + 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 + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(r1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + + if colours == 'bwy': + """Create image for only yellow pixels""" + buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white + buffer2[numpy.logical_and(g2 == 255, b2 == 0)] = [0,0,0] #yellow -> black + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(g1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + +if render == True: + epaper = driver.EPD() + print('Initialising E-Paper...', end = '') + epaper.init() + print('Done') + + print('Sending image data and refreshing display...', end='') + if three_colour_support == True: + epaper.display(epaper.getbuffer(black), epaper.getbuffer(colour)) + else: + epaper.display(epaper.getbuffer(black)) + print('Done') + + print('Sending E-Paper to deep sleep...', end = '') + epaper.sleep() +print('Done')