#!/usr/bin/python import datetime import imp import inspect import os import sys from math import floor from shutil import copyfile from time import sleep import pygame from PIL import Image try: imp.find_module('RPi') import RPi.GPIO as GPIO except ImportError: import FakeGPIO as GPIO try: imp.find_module('picamera') import picamera except ImportError: import FakePicamera as picamera __all__ = ["monotonic_time"] import ctypes CLOCK_MONOTONIC_RAW = 4 # see class timespec(ctypes.Structure): _fields_ = [ ('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long) ] librt = ctypes.CDLL('librt.so.1', use_errno=True) clock_gettime = librt.clock_gettime clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] def monotonic_time(): t = timespec() if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(t)) != 0: errno_ = ctypes.get_errno() raise OSError(errno_, os.strerror(errno_)) return t.tv_sec + t.tv_nsec * 1e-9 os.chdir(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) # CONFIGURATION WIDTH = 800 HEIGHT = 480 FONTSIZE = 500 PIC_INTERVAL = 0.5 TEXT_INTERVAL = 0.5 TEXT_POS = (250, -40) OVERLAY_ALPHA = 64 COLOR_BACK = pygame.Color(0, 0, 0) COLOR_FLASH = pygame.Color(255, 255, 255) GRAPHICS_DIR = "graphics" IMAGE_DIR = "images/0-CURRENT" COUNTDOWN = 3 SHOT_COUNT = 3 BORDER = 10 GPIO_LED_BORDER = 7 GPIO_SPOT_RIGHT = 35 GPIO_SPOT_LEFT = 37 GPIO_BUTTON_LED = 36 GPIO_BUTTON = 38 THUMB_WIDTH = int((800 - BORDER) / SHOT_COUNT - BORDER) THUMB_HEIGHT = int(THUMB_WIDTH / 1.333) IMAGES_ORIGINAL = os.path.join(IMAGE_DIR, "original") IMAGES_TODO = os.path.join(IMAGE_DIR, "todo") # global variables spot_mode = 0 session_start = monotonic_time() # GPIO helpers def led_border(state): global GPIO_LED_BORDER GPIO.output(GPIO_LED_BORDER, state) def set_spot_mode(new_mode): global spot_mode spot_mode = new_mode % 4 GPIO.output(GPIO_SPOT_LEFT, not (spot_mode & 1)) GPIO.output(GPIO_SPOT_RIGHT, not (spot_mode & 2)) def next_spot_mode(): global spot_mode set_spot_mode(spot_mode + 1) # user interface def button_press(gpio_id): next_spot_mode() # INIT GPIO GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup(GPIO_SPOT_RIGHT, GPIO.OUT, initial=GPIO.HIGH) GPIO.setup(GPIO_SPOT_LEFT, GPIO.OUT, initial=GPIO.HIGH) GPIO.setup(GPIO_LED_BORDER, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(GPIO_BUTTON_LED, GPIO.OUT, initial=GPIO.LOW) GPIO.setup(GPIO_BUTTON, GPIO.IN) GPIO.add_event_detect(GPIO_BUTTON, GPIO.FALLING, button_press, bouncetime=300) led_border(False) sleep(0.02) led_border(True) sleep(0.02) led_border(False) sleep(0.02) led_border(True) sleep(0.02) led_border(False) sleep(0.02) led_border(True) sleep(0.02) led_border(False) # INIT PYGAME pygame.init() pygame.mouse.set_visible(False) screen = pygame.display.set_mode((WIDTH, HEIGHT)) # LOAD GRAPHICS gfx = {} def _load(name): filename = "%s.png" % name path = os.path.join(GRAPHICS_DIR, filename) img = pygame.image.load(path) rect = img.get_rect() gfx[name] = (img, rect) _load("begin") _load("loading") _load("yes") _load("no") _load("end_yes") _load("end_no") _load("saved") _load("canceled") # LOAD COUNTDOWN OVERLAYS img_cdn = [] pad_cdn = [] for i in range(0, COUNTDOWN): filename = "%s.png" % (COUNTDOWN - i) path = os.path.join(GRAPHICS_DIR, filename) img = Image.open(path) pad = Image.new('RGB', ( ((img.size[0] + 31) // 32) * 32, ((img.size[1] + 15) // 16) * 16, )) pad.paste(img, (0, 0)) img_cdn.append(img) pad_cdn.append(pad) # FUNCTIONS ======================================================================================== def waitForEvent(): pygame.event.clear() while 1: sleep(0.25) events = pygame.event.get() if len(events) > 0: for event in events: if event.type == pygame.QUIT: sys.exit(0) return events def waitForTouch(): pos = (-1, -1) while True: events = waitForEvent() for event in events: if event.type == pygame.MOUSEMOTION: pos = event.pos elif event.type == pygame.MOUSEBUTTONUP: return pos def maintainance(): screen.fill(COLOR_BACK) screen.blit(maintain, maintain_rect) pygame.display.flip() # wait pos = waitForTouch() def waitForBegin(): screen.fill(COLOR_BACK) screen.blit(*gfx["begin"]) pygame.display.flip() # wait pos = waitForTouch() if pos[0] > 720 and pos[1] > 400: maintainance() def countdown(camera, pad, img): sleep(PIC_INTERVAL) o = camera.add_overlay(pad.tobytes(), size=img.size) o.alpha = OVERLAY_ALPHA o.layer = 3 sleep(TEXT_INTERVAL) camera.remove_overlay(o) def makePhotos(): # CLEAR SCREEN screen.fill(COLOR_BACK) pygame.display.update() # INIT CAMERA camera = picamera.PiCamera() camera.vflip = False camera.hflip = True camera.resolution = (2592, 1944) camera.rotation = 90 camera.start_preview() # COUNTDOWN led_border(True) for i in range(0, COUNTDOWN): countdown(camera, pad_cdn[i], img_cdn[i]) led_border(False) # SHOTS for i in range(0, SHOT_COUNT): # SHOT! sleep(PIC_INTERVAL) led_border(True) camera.capture("./tmp%s.jpg" % i, format='jpeg', quality=100, thumbnail=None) led_border(False) # PAUSE if i < SHOT_COUNT - 1: screen.fill(COLOR_BACK) pygame.display.update() camera.start_preview() sleep(TEXT_INTERVAL) # CLOSE CAMERA camera.close() def chooseImages(): # clear screen screen.fill(COLOR_BACK) screen.blit(*gfx["loading"]) pygame.display.update() # load images from disk img_tmp = [] rect_tmp = [] choices = [] x = 12 for i in range(0, SHOT_COUNT): img = pygame.image.load("tmp%s.jpg" % i) img = pygame.transform.scale(img, (THUMB_WIDTH, THUMB_HEIGHT)) rect = img.get_rect() rect.x = x rect.y = 146 img_tmp.append(img) rect_tmp.append(rect) choices.append(0) x = x + THUMB_WIDTH + BORDER save = False cancel = False any_choice = False while not cancel and (not save or not any_choice): # reset save (if nothing selected) save = False # draw background screen.fill(COLOR_BACK) if any_choice: screen.blit(*gfx["end_yes"]) else: screen.blit(*gfx["end_no"]) # draw images for i in range(0, SHOT_COUNT): screen.blit(img_tmp[i], rect_tmp[i]) # draw choices x = 12 for choice in choices: if choice > 0: (img, rect) = gfx["yes"] else: (img, rect) = gfx["no"] rect.x = x + (THUMB_WIDTH - rect.width) / 2 rect.y = 340 screen.blit(img, rect) x = x + THUMB_WIDTH + BORDER # update display pygame.display.flip() # wait for new event (touch screen / mouse / keyboard) pos = waitForTouch() if pos[1] > 140: # toggle image i = int(floor((pos[0] - BORDER / 2) / (THUMB_WIDTH + BORDER))) if i >= 0 and i < SHOT_COUNT: choices[i] = (choices[i] + 1) % 2 elif pos[1] < 100: # top menu buttons if pos[0] > 0 and pos[0] < 266: cancel = True elif pos[0] > 533 and pos[0] < 800 and (choices[0] > 0 or choices[1] > 0 or choices[2] > 0): save = True any_choice = False for choice in choices: if choice > 0: any_choice = True break return (choices, save) def saveImages(choices): local = datetime.datetime.now() session_age = (monotonic_time() - session_start) try: os.makedirs(IMAGES_ORIGINAL) except OSError: pass try: os.makedirs(IMAGES_TODO) except OSError: pass print("saving images...") for i in range(0, SHOT_COUNT): if choices[i] > 0: print(" saving image #%s" % i) filename = "%s-%s-session_age=%05d.jpg" % (local.strftime("%Y%m%dT%H:%M:%S.%f%z"), i, session_age) path_tmp = "tmp%s.jpg" % i path_original = os.path.join(IMAGES_ORIGINAL, filename) path_todo = os.path.join(IMAGES_TODO, filename) os.rename(path_tmp, path_original) copyfile(path_original, path_todo) try: while 1: waitForBegin() makePhotos() (choices, save) = chooseImages() if save: saveImages(choices); screen.fill(COLOR_BACK) screen.blit(*gfx["saved"]) pygame.display.flip() else: screen.fill(COLOR_BACK) screen.blit(*gfx["canceled"]) pygame.display.flip() sleep(2) except KeyboardInterrupt: print("") finally: led_border(False) pygame.quit()