#!/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 traceback import picamera import pygame import random import time import json __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 # 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" BASE_PATH = "/var/lib/photobox" COUNTDOWN = 3 SHOT_COUNT = 3 BORDER = 10 THUMB_WIDTH = int((800 - BORDER) / SHOT_COUNT - BORDER) THUMB_HEIGHT = int(THUMB_WIDTH / 1.333) CONFIG_LOCATION = "%s/config.json" % (BASE_PATH, ) os.chdir(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) # global variables session_start = monotonic_time() # INIT GPIO GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup(7, GPIO.OUT) GPIO.output(7, GPIO.LOW) sleep(0.02) GPIO.output(7, GPIO.HIGH) sleep(0.02) GPIO.output(7, GPIO.LOW) sleep(0.02) GPIO.output(7, GPIO.HIGH) sleep(0.02) GPIO.output(7, GPIO.LOW) sleep(0.02) GPIO.output(7, GPIO.HIGH) sleep(0.02) GPIO.output(7, GPIO.LOW) # INIT PYGAME pygame.init() pygame.mixer.quit() pygame.mouse.set_visible(False) screen = pygame.display.set_mode((WIDTH,HEIGHT)) # LOAD OVERLAYS loading = pygame.image.load(GRAPHICS_DIR + "/loading.png") loading_rect = loading.get_rect() begin = pygame.image.load(GRAPHICS_DIR + "/begin.png") begin_rect = begin.get_rect() yes = pygame.image.load(GRAPHICS_DIR + "/yes.png") yes_rect = yes.get_rect() yes_rect.y = 340 no = pygame.image.load(GRAPHICS_DIR + "/no.png") no_rect = no.get_rect() no_rect.y = 340 end_yes = pygame.image.load(GRAPHICS_DIR + "/end_yes.png") end_yes_rect = end_yes.get_rect() end_no = pygame.image.load(GRAPHICS_DIR + "/end_no.png") end_no_rect = end_no.get_rect() saved = pygame.image.load(GRAPHICS_DIR + "/saved.png") saved_rect = saved.get_rect() canceled = pygame.image.load(GRAPHICS_DIR + "/canceled.png") canceled_rect = canceled.get_rect() #~ img_info = Image.open(GRAPHICS_DIR + '/info.png') # LOAD COUNTDOWN OVERLAYS img_cdn = [] pad_cdn = [] for i in range(0, COUNTDOWN): img = Image.open(GRAPHICS_DIR + "/%s.png" % (COUNTDOWN - i)) 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) # LOAD INFORMATION OVERLAY img_info = Image.open(GRAPHICS_DIR + '/info.png') pad_info = Image.new('RGB', ( ((img_info.size[0] + 31) // 32) * 32, ((img_info.size[1] + 15) // 16) * 16, )) pad_info.paste(img_info, (0, 0)) # 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(begin, begin_rect) pygame.display.flip() # wait pos = waitForTouch() if pos[0] > 720 and pos[1] > 400: maintainance() def countdown(camera, pad, img, flash): sleep(PIC_INTERVAL) o = camera.add_overlay(pad.tostring(), size=img.size) o.alpha = OVERLAY_ALPHA o.layer = 3 # if flash: # GPIO.output(7, GPIO.HIGH) # sleep(0.01) # GPIO.output(7, GPIO.LOW) 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 # PREVIEW camera.start_preview() o = camera.add_overlay(pad_info.tostring(), size=img_info.size) o.alpha = 255 o.layer = 3 sleep(3) camera.remove_overlay(o) sleep(1) # COUNTDOWN GPIO.output(7, GPIO.HIGH) for i in range(0, COUNTDOWN): countdown(camera, pad_cdn[i], img_cdn[i], True) GPIO.output(7, GPIO.LOW) # SHOTS for i in range(0, SHOT_COUNT): # SHOT! sleep(PIC_INTERVAL) GPIO.output(7, GPIO.HIGH) screen.fill(COLOR_FLASH) pygame.display.update() camera.stop_preview() camera.capture("./tmp%s.jpg" % i, format='jpeg', quality=100, thumbnail=None) GPIO.output(7, GPIO.LOW) # 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(loading, loading_rect) 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 (due to no choice made) save = False # draw background screen.fill(COLOR_BACK) if any_choice: screen.blit(end_yes, end_yes_rect) else: screen.blit(end_no, end_no_rect) # 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: yes_rect.x = x + (THUMB_WIDTH - yes_rect.width) / 2 screen.blit(yes, yes_rect) else: no_rect.x = x + (THUMB_WIDTH - no_rect.width) / 2 screen.blit(no, no_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 # END: choice #~ print("# END: choice") return (choices, save) def saveImages(choices): local = datetime.datetime.now() session_age = (monotonic_time() - session_start) print("saving images...") for i in range(0, SHOT_COUNT): if choices[i] > 0: print(" saving image #%s" % i) EVENT_PATH = "%s/events/%s" % (BASE_PATH, EVENT_ID) try: os.makedirs(EVENT_PATH + "/original") except OSError: pass try: os.makedirs(EVENT_PATH + "/todo") except OSError: pass filename = "%s-%s-session_age=%05d.jpg" % (local.strftime("%Y%m%dT%H:%M:%S.%f%z"), i, session_age) os.rename("tmp%s.jpg" % i, EVENT_PATH + "/original/" + filename) copyfile(EVENT_PATH + "/original/" + filename, EVENT_PATH + "/todo/" + filename) # show message screen.fill(COLOR_BACK) screen.blit(saved, saved_rect) pygame.display.flip() sleep(2) def loadConfig(): global EVENT_ID try: if os.path.isfile(CONFIG_LOCATION): config = open(CONFIG_LOCATION, "r").read() config = json.loads(config) EVENT_ID = int(config["id"]) except: traceback.print_exc() EVENT_ID = -1 EVENT_ID = -1 while 1: waitForBegin() makePhotos() (choices, save) = chooseImages() loadConfig() if save: saveImages(choices); else: # show message screen.fill(COLOR_BACK) screen.blit(canceled, canceled_rect) pygame.display.flip() sleep(2) pygame.quit()