mit der Maus kann das Bild verschoben werden.
Code: Alles auswählen
#! /usr/bin/env python
# -*- coding: utf-8
from __future__ import division
import tkinter as tk
import os
import datetime
import cv2
from PIL import Image, ImageTk
from functools import partial
from itertools import cycle
import numpy as np
WIDTH = 640
HEIGHT = 480
VIDEO_CODEC = "XVID"
DEFAULT_CAM_ID = 0
class Microscope(object):
PROPID_WIDTH = 3
PROPID_HEIGHT = 4
INTERPOLATION_METHODS= {"NEAREST" : cv2.INTER_NEAREST,
"LINEAR" : cv2.INTER_LINEAR,
"AREA" : cv2.INTER_AREA,
"CUBIC" : cv2.INTER_CUBIC,
"LANCZOS4" : cv2.INTER_LANCZOS4}
def __init__(self, cam_id=DEFAULT_CAM_ID, number_of_imgs=10,
series_time_interval=1, image_path=".", video_path=".",
series_img_path=".", img_format=".tiff"):
self.cam = cv2.VideoCapture(cam_id)
if not self.cam.isOpened():
raise RuntimeError("can not open camera {0!r}".format(
cam_id))
self.img_format = img_format
self.inter_frame = None
self.last_frame = None
self.take_series = False
self.recording = False
self.number_of_imgs = number_of_imgs
self.series_counter = None
self.series_time_counter = None
self.series_time_interval = series_time_interval
self.image_path = image_path
self.video_path = video_path
self.series_img_path = series_img_path
self.series_dir = None
self.interpolation_methods_keys = cycle(
self.INTERPOLATION_METHODS.keys())
self.interpolation_method = self.interpolation_methods_keys.next()
self.cam_width = self.zoom_width = int(self.cam.get(self.PROPID_WIDTH))
self.cam_height = self.zoom_height = int(self.cam.get(self.PROPID_HEIGHT))
def __enter__(self):
return self
def __exit__(self, *args):
self.release()
@property
def size(self):
return self.cam_width, self.cam_height
@property
def zoom_size(self):
return self.zoom_width, self.zoom_height
@property
def interpolation(self):
return self.interpolation_method
def get_image(self):
state, frame = self.cam.read()
if state and np.array_equal(self.last_frame, frame):
self.release()
state = False
if not state:
raise RuntimeError("could not read image")
self.last_frame = frame
self.inter_frame = cv2.resize(frame, self.zoom_size,
interpolation = self.INTERPOLATION_METHODS[
self.interpolation_method])
if self.take_series:
self.series_time_counter += 1
if self.series_time_counter > self.series_time_interval:
self.series_counter += 1
self.series_time_counter = 0
if self.series_counter <= self.number_of_imgs:
img_name = "{0}{1}".format(self.series_counter,
self.img_format)
cv2.imwrite(os.path.join(self.series_img_path,
self.series_dir, img_name), self.inter_frame)
else:
self.take_series = False
if self.recording:
self.video_writer.write(self.inter_frame)
return self.inter_frame
def recording_start_stop(self, name = None):
if self.recording:
self.recording = False
else:
self.recording = True
self.video_writer = cv2.VideoWriter(os.path.join(
self.video_path, name if name else
"{0:%d%b%Y_%H_%M_%S.%f}.avi".format(datetime.datetime.utcnow()
)), cv2.cv.CV_FOURCC(* VIDEO_CODEC), 24, self.zoom_size)
def take_series_picture(self):
if not self.take_series:
self.series_dir = "{0:%d%b%Y_%H_%M_%S.%f}".format(
datetime.datetime.utcnow())
os.makedirs(self.series_dir)
self.series_time_counter = 0
self.series_counter = 0
self.take_series = True
def take_picture(self, name = None):
if not name:
name = "{0:%d%b%Y_%H_%M_%S.%f}{1}".format(
datetime.datetime.utcnow(), self.img_format)
cv2.imwrite(os.path.join(self.image_path, name), self.inter_frame)
def series_up_down(self, step):
if not self.take_series:
if self.number_of_imgs > -step:
self.number_of_imgs += step
def set_time_interval(self, step):
if not self.take_series:
if self.series_time_interval > -step:
self.series_time_interval += step
def zoom_image(self, step):
height = int(self.zoom_height + step)
width = int(height * self.cam_width / self.cam_height)
if width > 0 and height > 0:
self.zoom_width = width
self.zoom_height = height
def reset_zoom(self):
self.zoom_width, self.zoom_height = self.size
def next_interpolation_method(self):
self.interpolation_method = self.interpolation_methods_keys.next()
def release(self):
self.cam.release()
class MicroscopeUI(tk.Frame):
UPDATE_INTERVAL = 10
REC_ON = 5
def __init__(self, parent, microscope, width, height, zoom_step=10):
tk.Frame.__init__(self, parent)
self.parent = parent
self.width = width
self.height = height
self.tk_image = None
self.rec_on = cycle(x for x in xrange(self.REC_ON))
self.text_on = False
self.microscope = microscope
self.text_colour_index = cycle(["white", "green", "black", "red",
"magenta", "green", "brown", "yellow", "blue", "orange", "gray"])
self.text_colour = self.text_colour_index.next()
self.canvas = tk.Canvas(self, width=width, height=height)
self.canvas.grid(column=0, row=0)
self.canvas.bind("<ButtonPress-1>", self.start_slide)
self.canvas.bind("<B1-Motion>", self.slide_image)
self.vscrollbar = tk.Scrollbar(self)
self.vscrollbar.grid(column=1, row=0, sticky=tk.N+tk.S)
self.canvas.config(yscrollcommand=self.vscrollbar.set)
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
self.hscrollbar.grid(column=0, row=1, columnspan=5, sticky=tk.E+tk.W)
self.canvas.config(xscrollcommand=self.hscrollbar.set)
self.hscrollbar.config(command=self.canvas.xview)
button_frame = tk.Frame(self)
button_frame.grid(column=0, row=3, columnspan=2)
self.buttons = list()
for column, (text, width, command, var)in enumerate(
(("||", 2, self.capture_start_stop, ()),
("Z+", 2, self.microscope.zoom_image, (zoom_step,)),
("Z-", 2, self.microscope.zoom_image, (-zoom_step,)),
("Z+/-", 2, self.microscope.reset_zoom, ()),
("[1]", 2, self.microscope.take_picture, ()),
("REC", 2, self.recording_start_stop, ()),
("[S]]]", 2, self.microscope.take_series_picture, ()),
("INTPOL", 5, self.next_interpolation_method, ()),
("S+", 2, self.series_up_down, (1,)),
("S-", 2, self.series_up_down, (-1,)),
("T+", 2, self.time_interval_up_down, (
self.microscope.series_time_interval,)),
("T-", 2, self.time_interval_up_down, (
-self.microscope.series_time_interval,)),
("ON", 2, self.set_text_on_off, ()),
("C", 2, self.change_text_colour, ()))):
button = tk.Button(button_frame, text=text, width=width,
relief="raised", font="Arial 10 bold",
command=partial(command, *var))
button.grid(column=column, row=0)
self.buttons.append(button)
def slide_image(self, event):
self.canvas.scan_dragto(event.x, event.y, gain=-1)
def start_slide(self, event):
self.canvas.scan_mark(event.x, event.y)
def capture_start_stop(self):
if self.after_id is None:
self.buttons[0].config(text = "||")
self.run()
else:
self.buttons[0].config(text = ">")
self.after_cancel(self.after_id)
self.after_id = None
def run(self):
try:
tk_image = Image.frombytes("RGB", self.microscope.zoom_size,
self.microscope.get_image(), "raw", "BGR")
except RuntimeError:
self.raise_cam_id_error()
return
self.canvas.delete("img", "rec", "txt")
self.tk_image = ImageTk.PhotoImage(tk_image)
self.canvas.create_image((0,0), anchor=tk.NW,
image=self.tk_image, tag="img")
width, height = self.microscope.zoom_size
if self.text_on:
rec_text = " "
if self.microscope.recording and not self.rec_on.next():
rec_text = "O"
self.canvas.create_text(width / 2, height / 2,
text="+", font="Courier 30", fill=self.text_colour,
tag="txt")
self.canvas.create_text(width / 2,
10 + height/2 - self.height / 2,
font="Courier 13 bold",
text = "REC:{0} FILTER:{1} SERIES:{2} TIME:{3} RES:{4}:{5}"
.format(rec_text,
self.microscope.interpolation,
self.microscope.number_of_imgs,
self.microscope.series_time_interval,
width, height),
anchor="center",
fill = self.text_colour,
tag = "txt")
state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
for button in (1, 2, 3, 7, 8, 9, 10, 11):
self.buttons[button].config(state=state)
width, height = self.microscope.zoom_size
self.canvas.config(scrollregion = (0, 0, width, height))
self.after_id = self.after(self.UPDATE_INTERVAL, self.run)
def change_text_colour(self):
self.set_text_on_off(True)
self.text_colour = self.text_colour_index.next()
def set_text_on_off(self, force_on=False):
self.text_on = force_on or not self.text_on
self.buttons[12].config(text="OFF" if self.text_on else "ON")
def raise_cam_id_error(self):
self.canvas.delete("img", "rec", "txt")
self.canvas.create_text((self.width / 2, self.height / 2),
text='NO CAM', font='Arial 40')
for button in self.buttons:
button.config(state="disabled")
def recording_start_stop(self):
self.set_text_on_off(True)
self.microscope.recording_start_stop()
def series_up_down(self, step):
self.set_text_on_off(True)
self.microscope.series_up_down(step)
def time_interval_up_down(self, step):
self.set_text_on_off(True)
self.microscope.set_time_interval(step)
def next_interpolation_method(self):
self.set_text_on_off(True)
self.microscope.next_interpolation_method()
def release(self):
self.parent.destroy()
def main():
root = tk.Tk()
root.title('MICROSCOPE')
try:
with Microscope(series_time_interval=1) as microscope:
def take_picture(e):
microscope_ui.take_picture()
microscope_ui = MicroscopeUI(
root, microscope, WIDTH, HEIGHT)
microscope_ui.pack(expand=tk.YES)
microscope_ui.run()
root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
#root.bind("<Key" + chr(10) + ">", take_picture)
root.mainloop()
except RuntimeError:
tk.Label(root, text = 'can not open camera {0!r}'.format(
DEFAULT_CAM_ID), font = "Arial 20", height = 10).pack()
root.mainloop()
if __name__ == '__main__':
main()