#!/usr/bin/python import sys, os, inspect import pytz, datetime from time import sleep from PIL import Image from shutil import copyfile from math import floor import RPi.GPIO as GPIO import picamera import pygame import random import time # ~ import server __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.tostring(), 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()