#!/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
import getpass
OPERATOR = getpass.getuser()
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", "matthias", "felix", "patrickb",'patrickk','patrick','anne','stefan','margaret','michi','guenter','daniel']
_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, height=20)
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, height=20)
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}%\n'.format(cam_control_panel.sensitivity.value)
_str += '\n'
_str += 'date: {:s}\n'.format(ap.date)
_str += 'time: {:s}\n'.format(ap.time)
_str += 'operator: {:s}\n'.format(OPERATOR)
_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()