#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys #from tkinter import * import tkinter as tk from tkinter import ttk from PIL import ImageTk, Image import numpy as np import o3000 import cv2 from datetime import datetime import code # entering the interactive mode at a certain point: code.interact(local=dict(globals(), **locals())), Ctrl+D and the script goes on IMAGE_WIDTH = 1280 IMAGE_HEIGHT = 960 class ImageLabel(tk.Label): def __init__(self, *args, **kwargs): super(ImageLabel,self).__init__(*args, **kwargs) self.image_update(255*np.ones([IMAGE_HEIGHT,IMAGE_WIDTH,3], dtype=np.uint8)) def image_update(self, image): ''' :param image: A numpy array or a PIL image object, refere to https://pillow.readthedocs.io/en/stable/reference/Image.html ''' if isinstance(image, Image.Image): pass elif isinstance(image, np.ndarray): #code.interact(local=dict(globals(), **locals())) image = Image.fromarray(image) else: raise Exception("illegal image format, must be eighter a numpy array or a PIL image object") self._image = ImageTk.PhotoImage(image) self.configure(image=self._image) class StringEntry(tk.Entry): def __init__(self, master, *args, **kwargs): self._variable = tk.StringVar() self.name = '' super(StringEntry,self).__init__(master=master,text=self._variable, *args, **kwargs) @property def name(self): return self._variable.get() @name.setter def name(self, name): self._variable.set(name) class FloatEntry(tk.Entry): def __init__(self, master, scaling = 1.0, *args, **kwargs): self._scaling = scaling self._variable = tk.StringVar() self.value = '' vcmd = (master.register(self.validate_digit)) super(FloatEntry,self).__init__(master=master,text=self._variable, *args, **kwargs, validate='all', validatecommand=(vcmd, '%P')) @property def scaling(self): return self._scaling @property def value(self): try: return float(self._variable.get())/self.scaling except ValueError: return None @value.setter def value(self, value): if self.validate_digit(value): try: self._variable.set(float(value)*self.scaling) except ValueError as e: print('FloatEntry ignore value {}, it is not a float'.format(value)) else: print('FloatEntry ignore value {}'.format(value)) def validate_digit(self, P): if P == '': return True try: _val = float(P) if _val < 0.0: return False else: return True except ValueError: return False class ImageControlPanel(tk.Frame): def __init__(self, save_fcn, *args, **kwargs): super(ImageControlPanel,self).__init__(*args, **kwargs) tk.Label(self,text='Filename: ').grid(column=0, row=0) self.filename_entry = StringEntry(self, bg='white') self.filename_entry.grid(column=1, row=0) tk.Label(self,text='.tiff').grid(column=2, row=0) def btn_save_fcn(): save_fcn(self.filename_entry.name) self.save_btn = tk.Button(self, text="save", command=btn_save_fcn) self.save_btn.grid(column=1, row=1) class CamControlPanel(tk.Frame): def __init__(self, *args, **kwargs): super(CamControlPanel,self).__init__(*args, **kwargs) tk.Label(self, text="Sensitivity:").grid(column=0, row=0) self.sensitivity = FloatEntry(self, width=7, bg='white') self.sensitivity.grid(column=1, row=0) tk.Label(self, text="%").grid(column=2, row=0) tk.Label(self, text="Exposure Time:").grid(column=0, row=1) self.exposure_time = FloatEntry(self, scaling=1000.0, width=7, bg='white') self.exposure_time.grid(column=1, row=1) tk.Label(self, text="ms").grid(column=2, row=1) def configure(): o3000.video_xml_send("time".format(self.exposure_time.value)) o3000.video_xml_send("sensitivity{:f}".format(self.sensitivity.value)) self.btn_configure = tk.Button(self, text="send configuration", command=configure) self.btn_configure.grid(column=0, row=3, columnspan=3) # TODO TODO these are hardcoded default values self.exposure_time.value = 0.02 self.sensitivity.value = 1.5 configure() class AnnotationPanel(tk.Frame): _PERSONS = ["nobody", "kris", "stefan", "sophia", "jonas", "juerg", "petar"] _PERSONS = ["nobody", "kris", "stefan", "sophia", "jonas", "juerg", "petar"] _POSES = ["no pose", "sleeping", "typing", "writting", "tinkering"] def __init__(self, *args, **kwargs): super(AnnotationPanel,self).__init__(*args, **kwargs) tk.Label(self,text='Date:').grid(column=0, row=0) self.date_var = tk.StringVar() tk.Label(self,textvariable=self.date_var).grid(column=1, row=0) tk.Label(self,text='Time:').grid(column=0, row=1) self.time_var = tk.StringVar() tk.Label(self,textvariable=self.time_var).grid(column=1, row=1) tk.Label(self,text='Person:').grid(column=0, row=2, sticky='w') self.person_list = tk.Listbox(self, exportselection=False) self.person_list.grid(column=0, row=3) [self.person_list.insert(tk.END, item) for item in self._PERSONS] tk.Label(self,text='Pose:').grid(column=1, row=2, sticky='w') self.pose_list = tk.Listbox(self, selectmode=tk.SINGLE, exportselection=False) self.pose_list.grid(column=1, row=3) [self.pose_list.insert(tk.END, item) for item in self._POSES] tk.Label(self,text='Comment: ').grid(column=0, row=4, columnspan=2) self.comment_text = tk.Text(self, bg='white', width=50, height=5) self.comment_text.grid(column=0, row=5, columnspan=2) def _list_selection_get(self, listbox): ''' :param: a tkinter Listbox widget :return: text of selected line in the given Listbox widget ''' sel = listbox.curselection() if len(sel) == 0: return 'unkown' elif len(sel) == 1: return listbox.get(sel[0],sel[0])[0] else: raise Exception('only single selection allowed') def update_date_time(self): ''' Updates annotation information to the current date and time ''' now = datetime.now() self.date_var.set(now.strftime("%Y-%m-%d")) self.time_var.set(now.strftime("%H:%M:%S")) @property def pose(self): ''' :return: the selected pose name ''' return self._list_selection_get(self.pose_list) @property def person(self): ''' :return: the selected pose name ''' return self._list_selection_get(self.person_list) @property def date(self): return self.date_var.get() @property def time(self): return self.time_var.get() @property def comment(self): return self.comment_text.get(1.0,tk.END) def annotation_text(filenames, annotation_panel, cam_control_panel): ''' build the annotation text for the annotation file ''' ap = annotation_panel _str = 'raw camera data (not debayered): {:s}\n'.format(filenames[0]) _str += 'color image (without lense calibration): {:s}\n'.format(filenames[1]) _str += 'calibrated color image (lense calibrated): {:s}\n'.format(filenames[2]) _str += '\n' _str += 'camera: O-3020 (color, rolling shutter, serial no.: 10030)\n' _str += 'lense: C-Mount 6mm 1/2.7" IR MP, with IR cutoff filter\n' _str += 'exposure time: {:f}s\n'.format(cam_control_panel.exposure_time.value) _str += 'sensitivity: {:f}%'.format(cam_control_panel.sensitivity.value) _str += '\n' _str += 'date: {:s}\n'.format(ap.date) _str += 'time: {:s}\n'.format(ap.time) _str += '\n' _str += 'person: {:s}\n'.format(ap.person) _str += 'pose: {:s}\n'.format(ap.pose) _str += 'comment:\n' _str += ap.comment[:-1] return _str imgs = [None, None, None] def main(): o3000.video_init() o3000.video_xml_send(""); root = tk.Tk() root.title("0-3000 Image Recorder") # the geometry of the box which will be displayed on the screen root.geometry("1700x1000") image_label = ImageLabel() image_label.pack(side=tk.LEFT) control_panel = tk.Frame(root) control_panel.pack(side=tk.LEFT) tk.Label(control_panel, text='').pack(pady=20) # separater, vertical space tk.Label(control_panel, text='').pack(pady=20) # separater, vertical space tk.Label(control_panel, text='Camera Configuration').pack(anchor='w') cam_control_panel = CamControlPanel(master=control_panel) cam_control_panel.pack() tk.Label(control_panel, text='').pack(pady=20) # separater, vertical space tk.Label(control_panel, text='Annotation').pack(anchor='w') annotation_panel = AnnotationPanel(master=control_panel) annotation_panel.pack() def record_image(): global imgs imgs = o3000.video_images_get() image = imgs[2] image_label.image_update(image) annotation_panel.update_date_time() btn_record_image = tk.Button(control_panel, text="record/update image", command=record_image) btn_record_image.pack() def save_image(): ap = annotation_panel filenamebase = '{:s}_{:s}'.format(ap.date.strip('-'), ap.time.strip(':')) filenames = [ '{:s}_raw.tiff'.format(filenamebase), '{:s}_rgb.tiff'.format(filenamebase), '{:s}.tiff'.format(filenamebase), ] annotation_filename = '{:s}.txt'.format(filenamebase) _annotation_text = annotation_text(filenames, annotation_panel, cam_control_panel) print('save images and annotation file {:s}, annotation:'.format(annotation_filename)) print(_annotation_text) for filename,image, in zip(filenames,imgs): im = Image.fromarray(image) im.save(filename) with open(annotation_filename,'w') as f: f.write(_annotation_text) btn_save_image = tk.Button(control_panel, text="save image", command=save_image) btn_save_image.pack() root.mainloop() if __name__ == '__main__': main()