aboutsummaryrefslogtreecommitdiffstats
path: root/image-record-annotate.py
diff options
context:
space:
mode:
Diffstat (limited to 'image-record-annotate.py')
-rw-r--r--image-record-annotate.py332
1 files changed, 332 insertions, 0 deletions
diff --git a/image-record-annotate.py b/image-record-annotate.py
new file mode 100644
index 0000000..d206bca
--- /dev/null
+++ b/image-record-annotate.py
@@ -0,0 +1,332 @@
+#!/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("<camera><set><acquisition><mode>time</mode><time>{:f}</time></acquisition></set></camera>".format(self.exposure_time.value))
+ o3000.video_xml_send("<camera><set><acquisition><mode>sensitivity</mode><sensitivity>{:f}</sensitivity></acquisition></set></camera>".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("<camera><get><model_id></model_id></get></camera>");
+
+
+ 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()