def main(self): if self.__onRpi == True: self.__camera = piggyphoto.Camera() else: self.__camera = PseudoCamera() self.__camera.leave_locked() self.__camera.capture_preview("preview/preview.jpg") self.db = TinyDB("db.json") # -------- Main Program Loop ----------- while not self.__done: try: self.event_loop() self.render() if self.__state != ST_PREPRINT: pg.display.update() self.__clock.tick(self.__fps) except: pass pg.quit() GPIO.cleanup()
class Photobooth: def __init__(self): self.__onRpi = amIOnRpi() GPIO.setmode(GPIO.BCM) # basic initializations self.__done = False # Done? Exit. self.__state = ST_IDLE # global state self.__surface = None # main surface self.__cnt_start = None # countdown start timestamp self.__blink_start = dt.datetime.now() # blink timestamp self.__blink_sec = 0.5 self.__numberdisplay = MAXNumber() self.idleRenderStarted = False self.notificationActive = False self.notification = {} self.__startupDateTimeString = dt.datetime.now().strftime("%H%M%S") self.__serienCount = 0 # config IN/OUT self.__pin_dome_in = PBGPIO(17, "IN", self.button_pressed) self.__pin_orange_top_in = PBGPIO(4, "IN", self.button_pressed) self.__pin_orange_bottom_in = PBGPIO(16, "IN", self.button_pressed) self.__pin_green_in = PBGPIO(22, "IN", self.button_pressed) # IN # orange top : 22 # orange bottom: 4 # dome: 17 # green: 16 self.__pin_dome_out = PBGPIO(18, "OUT", "") self.__pin_orange_top_out = PBGPIO(23, "OUT", "") self.__pin_orange_bottom_out = PBGPIO(24, "OUT", "") self.__pin_green_out = PBGPIO(25, "OUT", "") # load config self.cfg = cfgp.ConfigParser() if os.path.isfile("booth.cfg"): self.cfg.read("booth.cfg") else: self.cfg.read("booth_default.cfg") self.__cfgs = self.cfg.items("shooting") self.__countdown = self.cfg.getint("shooting", "countdown") self.__countdown_follow = self.cfg.getint("shooting", "countdown_follow") self.__print_ctdn = self.cfg.getint("shooting", "print_screen") self.__prints_allowed = self.cfg.getint("shooting", "count_of_prints_allowed") self.__downloadurl = None try: self.__downloadurl = self.cfg.get("shooting", "downloadurl") except: pass self.__lastCollage = "" self.__absScriptPath = os.path.abspath(os.path.dirname(sys.argv[0])) # if SHOW_STYLING == True: # load style self.__load_style() # init mousebuttons self.__MB_down = False self.__MB_pos = (0, 0) # init pg pg.init() self.__screen_width = self.cfg.getint("booth", "screen_width") self.__screen_height = self.cfg.getint("booth", "screen_height") self.__screen_leftWidth = 70 self.__screen_rightWidth = self.__screen_width - self.__screen_leftWidth # prepare screen self.__screen_size = map(int, [self.__screen_width, self.__screen_height]) # screen = pg.display.set_mode(self.__screen_size) if self.__onRpi == True: screen = pg.display.set_mode(self.__screen_size, pg.FULLSCREEN) else: screen = pg.display.set_mode(self.__screen_size) # , pg.FULLSCREEN) pg.display.set_caption("Rocksack's Photobooth") # pg.mouse.set_visible(False) pg.mouse.set_pos((0, self.__screen_height)) # get main surface self.__surface = pg.display.get_surface() self.__leftSurface = pg.Surface((70, self.__screen_height)) self.__rightSurface = pg.Surface((self.__screen_width - 70, self.__screen_height)) # font for writing countdown numbers self.__cnt_font = pg.font.SysFont("monospace", 400) self.__cnt_font_medium = pg.font.SysFont("monospace", 100) self.__cnt_font_roboto_medium = pg.font.SysFont("Roboto-Regular", 100) self.__cnt_font_roboto_message = pg.font.SysFont("Roboto-Regular", 60) self.__cnt_font_roboto_message_sm = pg.font.SysFont("monospace", 20) # use clock to limit framerate self.__clock = pg.time.Clock() self.__fps = 60 self.__mkdir("preview") if ENABLE_GREENSCREEN == True: self.__bgimage = pg.transform.scale(pg.image.load("gfx/berge.jpg").convert_alpha(), [800, 600]) self.__libps = load_shared_lib("libpyslow") self.__gbf = self.__libps.greenbox self.__gbf.restype = None self.__gbf.argtypes = [numpy.ctypeslib.ndpointer(ctypes.c_int32), ctypes.c_int, ctypes.c_int] self.__zzf = self.__libps.sepia self.__zzf.restype = None self.__zzf.argtypes = [numpy.ctypeslib.ndpointer(ctypes.c_int32), ctypes.c_int, ctypes.c_int] self.__scalef = self.__libps.scale self.__scalef.restype = None self.__scalef.argtypes = [ numpy.ctypeslib.ndpointer(ctypes.c_int32), ctypes.c_int, ctypes.c_int, numpy.ctypeslib.ndpointer(ctypes.c_int32), ctypes.c_int, ctypes.c_int, ] def __mkdir(self, path): try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise def __load_style(self): style = self.cfg.get("shooting", "pic_style") pics = filter(lambda item: item[0][:3] == "pic", self.cfg.items(style)) pics.sort() self.__pics_pos = list() for p in pics: self.__pics_pos.append(map(int, p[1].split(","))) self.__num_pics = len(pics) self.__bgcolor = map(int, self.cfg.get(style, "bgcolor").split(",")) self.__bgimagepath = self.cfg.get(style, "bgimage") self.__view_box = map(int, self.cfg.get(style, "box").split(",")) self.__resolution = map(int, self.cfg.get(style, "resolution").split(",")) # self.__fgsmall = pg.transform.scale(pg.image.load(self.cfg.get(style, "fgimage")), self.__view_box[2:]) # self.__fg = pg.transform.scale(pg.image.load(self.cfg.get(style, "fgimage")), self.__resolution) def show(self, file): picture = pg.image.load(file) self.__surface.blit(picture, (30, 40)) pg.display.flip() def showFullscreen(self, file): if ENABLE_GREENSCREEN == True: picture = pg.transform.scale(pg.image.load(file).convert_alpha(), self.__screen_size) else: picture = pg.transform.scale(pg.image.load(file), self.__screen_size) self.__surface.blit(picture, (0, 0)) pg.display.flip() def showFullscreenWithoutResize(self, file): picture = pg.image.load(file) self.__surface.blit(picture, (0, 0)) pg.display.flip() def button_pressed(self, pinid): if pinid == self.__pin_dome_in.getPinId(): if self.__state == ST_IDLE: self.__state = ST_PRESHOOT elif pinid == self.__pin_green_in.getPinId(): if self.__state == ST_PREPRINT: self.__state = ST_PRINT elif pinid == self.__pin_orange_top_in.getPinId(): if self.__state == ST_PREPRINT: self.__count_prints = self.__count_prints + 1 if self.__count_prints > self.__prints_allowed: self.__count_prints = self.__prints_allowed elif pinid == self.__pin_orange_bottom_in.getPinId(): if self.__state == ST_PREPRINT: self.__count_prints = self.__count_prints - 1 if self.__count_prints < 1: self.__count_prints = 1 def event_loop(self): pg.event.pump() # if(self.__pin_dome_in.get()): # if self.__state == ST_IDLE: # self.__state = ST_PRESHOOT # if self.__pin_green_in.get(): # if self.__state == ST_PREPRINT: # self.__state = ST_PRINT # if self.__pin_orange_top_in.get(): # if self.__state == ST_PREPRINT: # self.__count_prints = self.__count_prints +1 # if(self.__count_prints>3): # self.__count_prints = 3 # if self.__pin_orange_bottom_in.get(): # if self.__state == ST_PREPRINT: # self.__count_prints = self.__count_prints -1 # if(self.__count_prints<1): # self.__count_prints = 1 for event in pg.event.get(): if event.type == pg.KEYDOWN: if event.key == ord("q"): self.__done = True elif event.key == ord("a"): if self.__state == ST_IDLE: self.__state = ST_PRESHOOT elif event.key == ord("p"): if self.__state == ST_PREPRINT: self.__state = ST_PRINT print("key: " + str(event.key)) elif event.type == pg.MOUSEBUTTONDOWN: self.__MB_down = True self.__MB_pos = event.pos elif event.type == pg.MOUSEBUTTONUP: self.__MB_down = False self.__MB_pos = event.pos if event.type == pg.QUIT: self.__camera.close() done = True def render_live_right(self, path): try: self.__camera.capture_preview(path) picture = pg.transform.flip( pg.transform.scale(pg.image.load(path), (self.__screen_size[0] - 70, self.__screen_size[1])), True, False, ) self.__rightSurface.blit(picture, (0, 0)) except: pass def render_live(self, path): try: self.__camera.capture_preview(path) if ENABLE_GREENSCREEN == True: picture = pg.transform.flip( pg.transform.scale(pg.image.load(path).convert_alpha(), self.__screen_size), True, False ) self.__surface.blit(self.__bgimage, [0, 0]) rgb = pg.surfarray.pixels2d(picture) self.__gbf(rgb, rgb.shape[0], rgb.shape[1]) # self.__zzf(rgb, rgb.shape[0], rgb.shape[1]) rgb = None else: picture = pg.transform.flip(pg.transform.scale(pg.image.load(path), self.__screen_size), True, False) self.__surface.blit(picture, (0, 0)) except: pass def render_preview(self): pic = len(self.__pics_pos) - self.__cnt_images - 1 if ENABLE_GREENSCREEN == True: picture = pg.transform.flip( pg.transform.scale( pg.image.load("preview/preview_{}.jpg".format(pic)).convert_alpha(), self.__screen_size ), True, False, ) else: picture = pg.transform.flip( pg.transform.scale(pg.image.load("preview/preview_{}.jpg".format(pic)), self.__screen_size), True, False ) rgb = pg.surfarray.pixels2d(picture) self.__gbf(rgb, rgb.shape[0], rgb.shape[1]) rgb = None self.__surface.blit(picture, (0, 0)) def print_picture(self): self.send_print(os.path.abspath(self.__lastCollage), self.__count_prints) def send_print(self, file, count): data = {"file": file, "count": count} if self.cfg.get("errorHandling", "pushoveruser") != "" and self.cfg.get("errorHandling", "pushovertoken") != "": data["perroruser"] = self.cfg.get("errorHandling", "pushoveruser") data["perrortoken"] = self.cfg.get("errorHandling", "pushovertoken") # self.__count_prints # self.__lastCollage print(json.dumps(data)) try: r = requests.post("http://localhost:38163/setJob", json=json.dumps(data)) response = r.json() Data = Query() self.db.update( {"printid": response.id}, Data.pictureid == "{0}_{1}".format(self.__startupDateTimeString, self.__serienCount), ) print(json.dumps(response)) except: pass def callSupport(self): if self.cfg.get("errorHandling", "pushoveruser") != "" and self.cfg.get("errorHandling", "pushovertoken") != "": client = pushClient( self.cfg.get("errorHandling", "pushoveruser"), api_token=self.cfg.get("errorHandling", "pushovertoken") ) client.send_message("Support needed.... :(", title="Support needed....") def extract_time(self, json): try: return int(json["time"]) except KeyError: return 0 def printLast(self): print(self.db.all()[0]["time"]) elements = sorted(self.db.all(), key=self.extract_time, reverse=True) print(elements[0]) self.send_print(os.path.abspath(elements[0]["file"]), 1) pass def __button(self, path, x, y, w, h, ic, ac, msg="", action=None): mouse = self.__MB_pos click = self.__MB_down # print(click) if x + w > mouse[0] > x and y + h > mouse[1] > y: if self.__MB_down: pg.draw.rect(self.__leftSurface, ic, (x, y, w, h)) else: self.__leftSurface.blit(pg.image.load("images/" + path), (x, y)) if self.__MB_down and action != None: self.notification["time"] = int(time.time()) self.notification["msg"] = msg action() else: self.__leftSurface.blit(pg.image.load("images/" + path), (x, y)) pass def __notification(self): now = int(time.time()) try: if self.notification["time"] + 10 > now: pg.draw.rect(self.__rightSurface, (0, 0, 0), (0, 0, self.__screen_rightWidth, 70)) print(self.notification["msg"]) lbl_cnt = self.__cnt_font_roboto_message.render(str(self.notification["msg"]), 1, (200, 200, 200)) self.__rightSurface.blit(lbl_cnt, (20, 10)) except: pass def __render_generating_text(self): background = pg.Surface(self.__screen_size) background.fill((255, 255, 255)) self.__surface.blit(background, (0, 0)) lbl_cnt = self.__cnt_font_roboto_medium.render(str("generating"), 1, (200, 0, 0)) self.__surface.blit(lbl_cnt, (0, 40)) lbl_cnt = self.__cnt_font_roboto_medium.render(str("collage..."), 1, (200, 0, 0)) self.__surface.blit(lbl_cnt, (0, 150)) pg.display.update() def __render_result(self): if ( not hasattr(self, "collageThread") or self.collageThread == None or (self.collageThread != None and not self.collageThread.isAlive()) ): print(dt.datetime.now()) outpath = "{0}/collage_{1}_{2}.jpg".format( self.__photopath, self.__startupDateTimeString, self.__serienCount ) self.__lastCollage = outpath backgroundPath = "{0}/{1}".format(self.__absScriptPath, self.__bgimagepath) # background = Image.open(backgroundPath) screenSize = (self.__screen_width, self.__screen_height) data = { "backgroundPath": backgroundPath, "outpath": outpath, "pics": [], "screenSize": screenSize, "previewMiniPath": "{0}/collage_mini_{1}_{2}.jpg".format( self.__photopath, self.__startupDateTimeString, self.__serienCount ), "previewPath": "preview/preview_tmp.jpg", } for pic in range(0, self.__num_pics): print("loading") pass picturePath = "{0}/{1}/image_{2}_{3}_{4}.jpg".format( self.__absScriptPath, self.__photopath, self.__startupDateTimeString, self.__serienCount, pic ) # foreground = Image.open(picturePath) size = ( (int(self.__pics_pos[pic][2]) - int(self.__pics_pos[pic][0])), (int(self.__pics_pos[pic][3]) - int(self.__pics_pos[pic][1])), ) # foreground.thumbnail(size,Image.ANTIALIAS) # background.paste(foreground, (int(self.__pics_pos[pic][0]), int(self.__pics_pos[pic][1]))) data["pics"].append( [ { "picturePath": picturePath, "size": size, "pos": (int(self.__pics_pos[pic][0]), int(self.__pics_pos[pic][1])), } ] ) self.collageThread = myThread(1, "collageThread", "collage", data) self.collageThread.start() # background.save('{}'.format(outpath)) # background.thumbnail(screenSize,Image.ANTIALIAS) # background.save('{}'.format('preview/preview_tmp.jpg')) print(dt.datetime.now()) # # todo render with imagemagick # # image = pg.Surface(self.__resolution) # image.fill(self.__bgcolor) # for pic in range(0, self.__num_pics): # rect = [0, 0, 0, 0] # for i in range(0, 4): # rect[i] = int(self.__pics_pos[pic][i] * self.__resolution[i & 1] / self.__view_box[i | 2]) # img = pg.transform.scale(pg.image.load("{0}/image_{1}.jpg".format(self.__photopath, pic)), rect[2:]) # image.blit(img, rect[:2]) # image.blit(self.__fg, [0, 0]) # pg.image.save(image, "{}/image.jpg".format(self.__photopath)) # pass def render(self): self.__surface.fill([0, 0, 0]) if self.__state == ST_SLEEP: pass elif self.__state == ST_IDLE: try: os.remove("preview/preview_tmp.jpg") except: pass self.__leftSurface = pg.Surface((70, self.__screen_height)) self.__rightSurface = pg.Surface((self.__screen_width - 70, self.__screen_height)) self.__numberdisplay.setTopNumber(0) self.__numberdisplay.setDownNumber(0) diff = dt.datetime.now() - self.__blink_start cnt = self.__blink_sec - diff.seconds if cnt < 0: self.__pin_dome_out.toggle() # self.__pin_green_out.toggle() # self.__pin_orange_bottom_out.toggle() # self.__pin_orange_top_out.toggle() # self.__numberdisplay.allPlusOne() self.__blink_start = dt.datetime.now() self.render_live_right("preview/preview.jpg") self.__notification() pg.event.pump() self.__button( "but_support.png", 0, 0, 70, 70, (0, 200, 0), (0, 255, 0), "Support wurde gerufen", self.callSupport ) self.__button( "but_printLast.png", 0, 70, 70, 70, (200, 0, 0), (255, 0, 0), "Letzte Collage gedruckt", self.printLast ) if self.__downloadurl != None: lbl_cnt = self.__cnt_font_roboto_message_sm.render(str(self.__downloadurl), 1, (200, 200, 200)) lbl_cnt = pg.transform.rotate(lbl_cnt, 90) self.__leftSurface.blit(lbl_cnt, (24, self.__screen_height - lbl_cnt.get_height() - 20)) self.__surface.blit(self.__leftSurface, (0, 0)) self.__surface.blit(self.__rightSurface, (70, 0)) elif self.__state == ST_PRESHOOT: self.__state = ST_SHOOT self.__serienCount = self.__serienCount + 1 now = dt.datetime.now() self.__photopath = dt.datetime.now().strftime("photos/%Y-%m-%d") self.__mkdir(self.__photopath) self.__cnt_images = len(self.__pics_pos) self.__cnt_start = dt.datetime.now() self.db.insert( { "time": int(time.time()), "file": "{0}/collage_{1}_{2}.jpg".format( self.__photopath, self.__startupDateTimeString, self.__serienCount ), } ) self.countdownStartetCounter = 0 elif self.__state == ST_SHOOT: # diff = dt.datetime.now() - self.__cnt_start pic = len(self.__pics_pos) - self.__cnt_images self.render_live("preview/preview_{0}.jpg".format(pic)) diff = dt.datetime.now() - self.__cnt_start if int(pic) != 0: cnt = self.__countdown_follow - diff.seconds if self.countdownStartetCounter == int(pic): self.countdownStartetCounter = self.countdownStartetCounter + 1 data = {"start": self.__cnt_start, "cd": self.__countdown_follow} self.countdownThread = myThread(2, "countdownThread", "countdown", data, self.__numberdisplay) self.countdownThread.start() else: cnt = self.__countdown - diff.seconds if self.countdownStartetCounter == 0: self.countdownStartetCounter = self.countdownStartetCounter + 1 data = {"start": self.__cnt_start, "cd": self.__countdown} self.countdownThread = myThread(2, "countdownThread", "countdown", data, self.__numberdisplay) self.countdownThread.start() if cnt > 0: pass # self.__numberdisplay.setTopNumber(cnt) # lbl_cnt = self.__cnt_font.render(str(max(0, cnt)), 1, (200, 0, 0)) # self.__surface.blit(lbl_cnt, (300, 40)) else: self.__numberdisplay.setTopNumber(0) self.__camera.capture_image( "{0}/image_{1}_{2}_{3}.jpg".format( self.__photopath, self.__startupDateTimeString, self.__serienCount, pic ) ) self.__numberdisplay.setDownNumber(pic + 1) self.__cnt_images = self.__cnt_images - 1 self.__state = ST_BACKVIEW self.__cnt_start = dt.datetime.now() elif self.__state == ST_BACKVIEW: time_back = self.cfg.getint("shooting", "backview") diff = dt.datetime.now() - self.__cnt_start diffms = int(time_back - diff.seconds * 1000 - diff.microseconds / 1000) if SHOW_STYLING == True: time_mosaic = self.cfg.getint("shooting", "backview_mosaic") time_anim = self.cfg.getint("shooting", "backview_animate") rect = pg.draw.rect(self.__surface, self.__bgcolor, [200, 0, 400, 600]) for pic in range(0, len(self.__pics_pos) - self.__cnt_images - 1): pics_pos = self.__pics_pos[pic][:] pics_pos[0] = self.__pics_pos[pic][0] + 200 picture = pg.transform.flip( pg.transform.scale(pg.image.load("preview/preview_{0}.jpg".format(pic)), pics_pos[2:]), True, False, ) self.__surface.blit(picture, pics_pos[:2]) if diffms >= time_anim: self.render_preview() elif diffms >= 0: progress = max(0, (diffms - time_mosaic) / (time_anim - time_mosaic)) pic = len(self.__pics_pos) - self.__cnt_images - 1 pics_pos = self.__pics_pos[pic][:] pics_pos[0] = self.__pics_pos[pic][0] + 200 coords = map( lambda x: int(x[0] * (1 - progress) + x[1] * progress), zip(pics_pos, [0, 0, self.__screen_size[0], self.__screen_size[1]]), ) # picture = pg.transform.scale(pg.image.load("preview_{0}.jpg".format(pic)), coords[2:]) if ENABLE_GREENSCREEN == True: srcpic = pg.image.load("preview/preview_{}.jpg".format(pic)).convert_alpha() else: srcpic = pg.image.load("preview/preview_{}.jpg".format(pic)) srcrgb = pg.surfarray.pixels2d(srcpic) picture = pg.Surface(coords[2:]) dstrgb = pg.surfarray.pixels2d(picture) self.__scalef(srcrgb, srcpic.get_width(), srcpic.get_height(), dstrgb, coords[2], coords[3]) srcrgb = None dstrgb = None self.__surface.blit(picture, coords[:2]) self.__surface.blit(self.__fgsmall, [200, 0]) else: if self.__cnt_images > 0: self.__state = ST_SHOOT self.__cnt_start = dt.datetime.now() else: self.__render_result() self.__state = ST_PREPRINT self.__blink_start = dt.datetime.now() self.__print_start = dt.datetime.now() self.__count_prints = 1 else: if diffms >= 0: backview_border = self.cfg.getint("shooting", "backview_border") pic = len(self.__pics_pos) - self.__cnt_images - 1 pics_pos = [ backview_border, backview_border, (self.__screen_width - (backview_border * 2)), (self.__screen_height - (backview_border * 2)), ] picture = pg.transform.flip( pg.transform.scale(pg.image.load("preview/preview_{0}.jpg".format(pic)), pics_pos[2:]), True, False, ) self.__surface.blit(picture, pics_pos[:2]) else: if self.__cnt_images > 0: self.__state = ST_SHOOT self.__cnt_start = dt.datetime.now() else: # self.__render_result() self.collageThread = None self.__state = ST_GENCOLLAGE self.__blink_start = dt.datetime.now() self.__print_start = dt.datetime.now() self.__count_prints = 1 elif self.__state == ST_GENCOLLAGE: self.__render_generating_text() if self.collageThread == None or not self.collageThread.isAlive(): self.collageThread = None if os.path.isfile("preview/preview_tmp.jpg"): self.__state = ST_PREPRINT else: self.__render_result() elif self.__state == ST_PREPRINT: # self.render_live(self.__lastCollage) # self.showFullscreen(self.__lastCollage) self.showFullscreenWithoutResize("preview/preview_tmp.jpg") diff = dt.datetime.now() - self.__blink_start cnt = self.__blink_sec - diff.seconds if cnt < 0: # self.__pin_dome_out.toggle() # self.__pin_green_out.toggle() self.__pin_orange_top_out.toggle() # self.__camera.capture_preview('preview/preview_tmp.jpg') self.__blink_start = dt.datetime.now() # self.__state = ST_IDLE diff = dt.datetime.now() - self.__print_start cnt = self.__print_ctdn - diff.seconds # print(cnt) self.__numberdisplay.setTopNumber(int(cnt)) if cnt < 0: self.__numberdisplay.setTopNumber(0) self.__numberdisplay.setDownNumber(0) self.__state = ST_IDLE self.__pin_orange_top_out.reset() pass else: # todo show text pass self.__numberdisplay.setDownNumber(self.__count_prints) elif self.__state == ST_PRINT: self.print_picture() # for pic in range(0, self.__count_prints): # subprocess.call("lp -d {0} -n{1} {2}".format(self.cfg.get("booth", "printername"), 2, self.__lastCollage), shell=True) print("print") self.__state = ST_IDLE def main(self): if self.__onRpi == True: self.__camera = piggyphoto.Camera() else: self.__camera = PseudoCamera() self.__camera.leave_locked() self.__camera.capture_preview("preview/preview.jpg") self.db = TinyDB("db.json") # -------- Main Program Loop ----------- while not self.__done: try: self.event_loop() self.render() if self.__state != ST_PREPRINT: pg.display.update() self.__clock.tick(self.__fps) except: pass pg.quit() GPIO.cleanup()