Mikroskop mit Tkinter-GUi

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

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()
Gruß
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

es kann jetzt mit dem Button "AR" das Seitenverhältnis geändert werden und diese Funktion ist für Mikroskopaufnahmen völlig unnötig. Da auch alle anderen USB-Kameras erkannt werden ist es evt. doch brauchbar. Ich habe ein China Cinch/USB-Konverter verwendet und eine alte Videokamera ausgelesen.

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}
                            
    ASPECT_RATIO = {"16/9" : 9/16,
                    "4/3" : 3/4,
                    "3/2" : 2/3,
                    "21/9" : 9/21,
                    "8/3" : 3/8,
                    "9/5" : 9/5,
                    "3/1" : 1/3,
                    "25/12" : 12/25,
                    "25/16" : 16/25}
    

    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 = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = self.aspect_ratio_keys.next()
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.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.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        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.get_image)
                else:
                    self.take_series = False
        if self.recording:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
            self.video_writer.write(self.crop_frame)
            
        return self.crop_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.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.crop_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.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = self.interpolation_methods_keys.next()
        
    def next_aspect_ratio(self):
        self.aspect_ratio = self.aspect_ratio_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)
        vscrollbar = tk.Scrollbar(self)
        vscrollbar.grid(column=1, row=0, sticky=tk.N+tk.S)
        self.canvas.config(yscrollcommand=vscrollbar.set)
        vscrollbar.config(command=self.canvas.yview)
        hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        hscrollbar.grid(column=0, row=1, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=hscrollbar.set)
        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, ()),
             ("INTPOL", 5, self.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("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, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            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:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                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")
        if self.text_on:        
            rec_text = " "
            if self.microscope.recording and not self.rec_on.next():
                rec_text = "*"
            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 
                - height / 2,
                font="Courier 13 bold",
                text = "REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3}  RES:{4}:{5} > {6}"
                .format(rec_text,
                    self.microscope.interpolation,
                    self.microscope.number_of_imgs,                            
                    self.microscope.series_time_interval,
                    width, height,
                    self.microscope.aspect_ratio),
                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, 14):
            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()
Gruß Frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

einige Fehler gefunden und den Text auf ein Label verschoben.

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}
                            
    ASPECT_RATIO = {"16/9" : 9/16,
                    "4/3" : 3/4,
                    "3/2" : 2/3,
                    "21/9" : 9/21,
                    "8/3" : 3/8,
                    "9/5" : 5/9,
                    "3/1" : 1/3,
                    "25/12" : 12/25,
                    "25/16" : 16/25,
                    "3/1" : 1/3,
                    "5/3" : 3/5,
                    "19/10" : 10/19,
                    "25/16" : 16/25,
                    "43/18" : 18/43,
                    "16/10" : 10/16}
    

    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 = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = self.aspect_ratio_keys.next()
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.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.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        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.crop_frame)
                else:
                    self.take_series = False
                    
        if self.recording:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
            self.video_writer.write(self.crop_frame)
        return self.crop_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.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.crop_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.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = self.interpolation_methods_keys.next()
        
    def next_aspect_ratio(self):
        self.aspect_ratio = self.aspect_ratio_keys.next()
        
    def release(self):
        self.cam.release()

class MicroscopeUI(tk.Frame):

    UPDATE_INTERVAL = 1
    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.cross_on = False
        self.microscope = microscope
        self.cross_colour_index = cycle(["white", "green", "black", "red",
            "magenta", "green", "brown", "yellow", "blue", "orange", "gray"])
        self.cross_colour = self.cross_colour_index.next()
        self.info_text = tk.Label(self, font="Courier 12")
        self.info_text.grid(column=0, row=0)
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.grid(column=0, row=1)
        self.canvas.bind("<ButtonPress-1>", self.start_slide)
        self.canvas.bind("<B1-Motion>", self.slide_image)
        hscrollbar = tk.Scrollbar(self)
        hscrollbar.grid(column=1, row=1, sticky=tk.N+tk.S)
        self.canvas.config(yscrollcommand=hscrollbar.set)
        hscrollbar.config(command=self.canvas.yview)
        vscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        vscrollbar.grid(column=0, row=2, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=vscrollbar.set)
        vscrollbar.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.microscope.recording_start_stop, ()),
             ("INTPOL", 5, self.microscope.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("S+", 2, self.microscope.series_up_down, (1,)),
             ("S-", 2, self.microscope.series_up_down, (-1,)),
             ("T+", 2, self.microscope.set_time_interval, (
              self.microscope.series_time_interval,)),
             ("T-", 2, self.microscope.set_time_interval, (
              -self.microscope.series_time_interval,)),
             ("ON", 2, self.set_cross_on_off, ()),
             ("C", 2, self.change_cross_colour, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            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:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                image, "raw", "BGR")
        except RuntimeError:
            self.raise_cam_id_error()
            return
            
        self.canvas.delete("img", "rec")
        self.tk_image = ImageTk.PhotoImage(tk_image)
        self.canvas.create_image((self.width / 2,self.height / 2), 
            anchor=tk.CENTER, image=self.tk_image, tag="img")
        if self.cross_on:
            self.canvas.create_text(width / 2, height / 2,
                text="+", font="Courier 30", fill=self.cross_colour,
                tag="rec", anchor=tk.CENTER)

        rec_text = "#" if self.microscope.recording \
            and self.rec_on.next() == 0 else " "
        self.info_text.config(text="REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3} RES:{4}:{5} > {6}"
            .format(
            rec_text,
            self.microscope.interpolation,
            self.microscope.number_of_imgs,                            
            self.microscope.series_time_interval,
            width, height,
            self.microscope.aspect_ratio))
 
        series_state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
        for button in (1, 2, 3, 6, 7, 8, 9, 10, 11, 14):
            self.buttons[button].config(state=series_state)
               
        rec_state = tk.DISABLED if self.microscope.recording else tk.NORMAL
        for button in (1, 2, 3, 6, 14):
            self.buttons[button].config(state=rec_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_cross_colour(self):
        self.cross_colour = self.cross_colour_index.next()
        
    def set_cross_on_off(self):
        self.cross_on = not self.cross_on
        self.buttons[12].config(text="OFF" if self.cross_on else "ON")
        
    def raise_cam_id_error(self):
        self.canvas.delete("img", "rec")
        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 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()
Die Codebox macht irgendwie Leerzeilen rein ?

Gruß
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@kaytec: ja, weil Zeile 137 viele Leerzeichen am Ende hat.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo Sirius3,

die sehe ich bei mir nicht so uns beim Anschalten von "LF" sind die auch nicht vorhanden. Es wird so sein und wie kann ich ich Unbrüche im Text mit "\"machen, da ich über 79 Zeichen komme ? Snafu hatte ich übersehen und er würde es auf GitHub einstellen, doch hätten auch noch andere User Interesse an dem Code ? Dazu gehört ja auch eine professionelle Dokumentation ... - das würde problematisch werden, denn mein Englischkenntnisse sind rundimetär und es würde keine Hilfe sein.

Gruß Frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

hat sich was bei OpenCV geändert.

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, ImageDraw, ImageFont
from functools import partial
from itertools import cycle
import numpy as np

WIDTH = 640
HEIGHT = 480
VIDEO_CODEC = "XVID"
DEFAULT_CAM_ID = 1


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}
                            
    ASPECT_RATIO = {"16/9" : 9/16,
                    "4/3" : 3/4,
                    "3/2" : 2/3,
                    "21/9" : 9/21,
                    "8/3" : 3/8,
                    "9/5" : 5/9,
                    "3/1" : 1/3,
                    "25/12" : 12/25,
                    "25/16" : 16/25,
                    "3/1" : 1/3,
                    "5/3" : 3/5,
                    "19/10" : 10/19,
                    "25/16" : 16/25,
                    "43/18" : 18/43,
                    "16/10" : 10/16,
                    "16/9" : 9/16,
                    "14/9" : 9/14}
    

    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 = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = self.aspect_ratio_keys.next()
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.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.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        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.crop_frame)
                else:
                    self.take_series = False
                    
        if self.recording:
            self.video_writer.write(self.crop_frame)
        return self.crop_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.VideoWriter_fourcc(* VIDEO_CODEC), 24, self.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.crop_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.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = self.interpolation_methods_keys.next()
        
    def next_aspect_ratio(self):
        self.aspect_ratio = self.aspect_ratio_keys.next()
        
    def release(self):
        self.cam.release()

class MicroscopeUI(tk.Frame):

    UPDATE_INTERVAL = 1
    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.cross_on = False
        self.microscope = microscope
        self.cross_colour_index = cycle([(0, 0, 0), (255, 0, 0), 
            (0, 0, 255), (0, 255, 0), (255, 255, 255)])
        self.cross_colour = self.cross_colour_index.next()
        self.info_text = tk.Label(self, font="mono 12")
        self.info_text.grid(column=0, row=0)
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.grid(column=0, row=1)
        self.canvas.bind("<ButtonPress-1>", self.start_slide)
        self.canvas.bind("<B1-Motion>", self.slide_image)
        hscrollbar = tk.Scrollbar(self)
        hscrollbar.grid(column=1, row=1, sticky=tk.N+tk.S+tk.E)
        self.canvas.config(yscrollcommand=hscrollbar.set)
        hscrollbar.config(command=self.canvas.yview)
        vscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        vscrollbar.grid(column=0, row=2, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=vscrollbar.set)
        vscrollbar.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.microscope.recording_start_stop, ()),
             ("INTPOL", 5, self.microscope.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("S+", 2, self.microscope.series_up_down, (1,)),
             ("S-", 2, self.microscope.series_up_down, (-1,)),
             ("T+", 2, self.microscope.set_time_interval, (
              self.microscope.series_time_interval,)),
             ("T-", 2, self.microscope.set_time_interval, (
              -self.microscope.series_time_interval,)),
             ("ON", 2, self.set_cross_on_off, ()),
             ("C", 2, self.change_cross_colour, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            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:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                image, "raw", "BGR")
            if self.cross_on:
                ImageDraw.Draw(tk_image).text((width / 2, height / 2), "+", 
                    self.cross_colour,ImageFont.truetype("FreeSans.ttf", 30))
        except RuntimeError:
            self.raise_cam_id_error()
            return
        self.canvas.delete("img")
        self.tk_image = ImageTk.PhotoImage(tk_image)
        self.canvas.create_image((self.width / 2,self.height / 2), 
            anchor=tk.CENTER, image=self.tk_image, tag="img")

        rec_text = "#" if self.microscope.recording \
            and self.rec_on.next() == 0 else " "
        self.info_text.config(text="REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3} RES:{4}:{5} > {6}"
            .format(
            rec_text,
            self.microscope.interpolation,
            self.microscope.number_of_imgs,                            
            self.microscope.series_time_interval,
            width, height,
            self.microscope.aspect_ratio))
 
        series_state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
        for button in (1, 2, 3, 6, 7, 8, 9, 10, 11, 14):
            self.buttons[button].config(state=series_state)
               
        rec_state = tk.DISABLED if self.microscope.recording else tk.NORMAL
        for button in (1, 2, 3, 6, 14):
            self.buttons[button].config(state=rec_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_cross_colour(self):
        self.cross_colour = self.cross_colour_index.next()
        
    def set_cross_on_off(self):
        self.cross_on = not self.cross_on
        self.buttons[12].config(text="OFF" if self.cross_on else "ON")
        
    def raise_cam_id_error(self):
        self.canvas.delete("img", "rec")
        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 release(self):
        self.parent.destroy()

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    root.resizable(0, 0)
    
    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()
            microscope_ui.run()
            root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
            root.bind("<Key" + chr(10) + ">", microscope.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()
Gruß
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kaytec: Was mir beim drüberschauen aufgefallen ist: Es wird nirgends die `next()`-Funktion verwendet, immer eine `next()`-Methode. Die gibt es im Gegensatz zu der Funktion in Python 3 nicht mehr. Das wäre also eine einfache Änderung um das später leichter portieren zu können.

In den Fällen wo `cycle()` auf Schlüssel aus Wörterbüchern angewendet wird, würde ich noch ein `sorted()` einbauen, sonst kann die Reihenfolge bei jedem Programmablauf anders sein, was die Benutzer eventuell verwirrt.

Bei `ASPECT_RATIO` würde ich auch die Quelltextzeilen im Editor sortieren. Das ist ja so viel, dass Dir anscheinend selbst nicht aufgefallen ist, dass da drei Einträge doppelt vorhanden sind.

Da Schlüssel und Wert jeweils aus den gleichen zwei Zahlen hergeleitet sind, würde ich die aus eben jenen Zahlen erzeugen, was die Gefahr von Flüchtigkeitsfehlern bei den redundanten Werten reduziert. Also beispielsweise:

Code: Alles auswählen

    ASPECT_RATIO = {
        '{}/{}'.format(a, b): b / a
        for a, b in [
            (3, 1),
            (3, 2),
            (4, 3),
            (5, 3),
            (8, 3),
            (9, 5),
            (14, 9),
            (16, 10),
            (16, 9),
            (19, 10),
            (21, 9),
            (25, 12),
            (25, 16),
            (43, 18),
        ]
    }
In `MicroscopeUI` werden ein paar Attribute ausserhalb der `__init__()` definiert.

Die lokale Funktion `take_picture()` in der `main()`-Funktion wird nirgends verwendet‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo __blackjack__,
danke für deine konstruktive Kritik. Die Funktion "take_picture" ist noch von vorhergehenden Testläufen und habe ich vergessen.

Funktion / Methode "next()"....? Muß ich mal mal nachlesen.

Die Einträge von "ASPECT_RATIO" habe ich mir im Netz zusammengesucht und reinkopiert - nachprüfen könnte helfen. Deine Variante ist natürlich sehr schön und ich habe sie eingebastelt.

Gruß Frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

UPDATE_INTERVAL = 100
REC_ON = 5

habe ich zur besseren Auffindbarkeit so gelassen, da die Schaltdauer des Aufnahmesymbols von der Dauer des Updates abhängig ist. - sollten besser als Konstante in _init_() ?

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, ImageDraw, ImageFont
from functools import partial
from itertools import cycle
import numpy as np

WIDTH = 640
HEIGHT = 480
VIDEO_CODEC = "XVID"
DEFAULT_CAM_ID = 1


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}
                            
    ASPECT_RATIO = {
        '{}/{}'.format(a, b): b / a
        for a, b in [
            (3, 1),
            (3, 2),
            (4, 3),
            (5, 3),
            (8, 3),
            (9, 5),
            (14, 9),
            (16, 10),
            (16, 9),
            (19, 10),
            (21, 9),
            (25, 12),
            (25, 16),
            (43, 18),
        ]
    }
    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 = next(self.interpolation_methods_keys)
        self.cam_width = self.zoom_width = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = next(self.aspect_ratio_keys)
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.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.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        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.crop_frame)
                else:
                    self.take_series = False
                    
        if self.recording:
            self.video_writer.write(self.crop_frame)
        return self.crop_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.VideoWriter_fourcc(* VIDEO_CODEC), 24, self.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.crop_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.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = next(self.interpolation_methods_keys)
        
    def next_aspect_ratio(self):
        self.aspect_ratio = next(self.aspect_ratio_keys)
        
    def release(self):
        self.cam.release()

class MicroscopeUI(tk.Frame):

    UPDATE_INTERVAL = 100
    REC_ON = 5
    
    def __init__(self, parent, microscope, width, height, update_interval=100,
            rec_on = 5, zoom_step=10):
        tk.Frame.__init__(self, parent)
        self.update_interval = update_interval
        self.parent = parent
        self.width = width
        self.height = height
        self.tk_image = None
        self.rec_on = cycle(x for x in xrange(rec_on))
        self.cross_on = False
        self.microscope = microscope
        self.cross_colour_index = cycle([(0, 0, 0), (255, 0, 0), 
            (0, 0, 255), (0, 255, 0), (255, 255, 255)])
        self.cross_colour = self.cross_colour_index.next()
        self.info_text = tk.Label(self, font="mono 12")
        self.info_text.grid(column=0, row=0)
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.grid(column=0, row=1)
        self.canvas.bind("<ButtonPress-1>", self.start_slide)
        self.canvas.bind("<B1-Motion>", self.slide_image)
        hscrollbar = tk.Scrollbar(self)
        hscrollbar.grid(column=1, row=1, sticky=tk.N+tk.S+tk.E)
        self.canvas.config(yscrollcommand=hscrollbar.set)
        hscrollbar.config(command=self.canvas.yview)
        vscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        vscrollbar.grid(column=0, row=2, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=vscrollbar.set)
        vscrollbar.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.microscope.recording_start_stop, ()),
             ("INTPOL", 5, self.microscope.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("S+", 2, self.microscope.series_up_down, (1,)),
             ("S-", 2, self.microscope.series_up_down, (-1,)),
             ("T+", 2, self.microscope.set_time_interval, (
              self.microscope.series_time_interval,)),
             ("T-", 2, self.microscope.set_time_interval, (
              -self.microscope.series_time_interval,)),
             ("ON", 2, self.set_cross_on_off, ()),
             ("C", 2, self.change_cross_colour, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            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:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                image, "raw", "BGR")
            if self.cross_on:
                ImageDraw.Draw(tk_image).text((width / 2, height / 2), "+", 
                    self.cross_colour,ImageFont.truetype("FreeSans.ttf", 30))
        except RuntimeError:
            self.raise_cam_id_error()
            return
        self.canvas.delete("img")
        self.tk_image = ImageTk.PhotoImage(tk_image)
        self.canvas.create_image((self.width / 2,self.height / 2), 
            anchor=tk.CENTER, image=self.tk_image, tag="img")

        rec_text = "#" if self.microscope.recording \
            and next(self.rec_on) == 0 else " "
        self.info_text.config(text="REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3} RES:{4}:{5} > {6}"
            .format(
            rec_text,
            self.microscope.interpolation,
            self.microscope.number_of_imgs,                            
            self.microscope.series_time_interval,
            width, height,
            self.microscope.aspect_ratio))
 
        series_state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
        for button in (1, 2, 3, 6, 7, 8, 9, 10, 11, 14):
            self.buttons[button].config(state=series_state)
               
        rec_state = tk.DISABLED if self.microscope.recording else tk.NORMAL
        for button in (1, 2, 3, 6, 14):
            self.buttons[button].config(state=rec_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_cross_colour(self):
        self.cross_colour = self.cross_colour_index.next()
        
    def set_cross_on_off(self):
        self.cross_on = not self.cross_on
        self.buttons[12].config(text="OFF" if self.cross_on else "ON")
        
    def raise_cam_id_error(self):
        self.canvas.delete("img", "rec")
        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 release(self):
        self.parent.destroy()

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    root.resizable(0, 0)
    
    try:
        with Microscope(series_time_interval=1) as microscope:
            def take_picture(e):
                microscope_ui.take_picture()
            microscope_ui = MicroscopeUI(
                root, microscope, WIDTH, HEIGHT, 200, 3)
            microscope_ui.pack()
            microscope_ui.run()
            root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
            root.bind("<Key" + chr(10) + ">", microscope.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()

Gruß Frank
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die Konstanten sollte man dann auch in der Signatur von `__init__()` verwenden, statt die Werte dort noch einmal zu wiederholen.

Für `MicroscopeUI.cross_color_index` wird immer noch die `next()`-Methode verwendet.

Und `take_picture()` ist auch immer noch da. :-)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke __blackjack__ .

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, ImageDraw, ImageFont
from functools import partial
from itertools import cycle
import numpy as np

WIDTH = 640
HEIGHT = 480
VIDEO_CODEC = "XVID"
DEFAULT_CAM_ID = 1


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}
                            
    ASPECT_RATIO = {
        '{}/{}'.format(a, b): b / a
        for a, b in [
            (3, 1),
            (3, 2),
            (4, 3),
            (5, 3),
            (8, 3),
            (9, 5),
            (14, 9),
            (16, 10),
            (16, 9),
            (19, 10),
            (21, 9),
            (25, 12),
            (25, 16),
            (43, 18),
        ]
    }
    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 = next(self.interpolation_methods_keys)
        self.cam_width = self.zoom_width = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = next(self.aspect_ratio_keys)
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.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.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        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.crop_frame)
                else:
                    self.take_series = False
                    
        if self.recording:
            self.video_writer.write(self.crop_frame)
        return self.crop_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.VideoWriter_fourcc(* VIDEO_CODEC), 24, self.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.crop_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.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = next(self.interpolation_methods_keys)
        
    def next_aspect_ratio(self):
        self.aspect_ratio = next(self.aspect_ratio_keys)
        
    def release(self):
        self.cam.release()
        
        
class MicroscopeUI(tk.Frame):
    
    def __init__(self, parent, microscope, width, height, update_interval=100,
            rec_on = 5, zoom_step=10):
        tk.Frame.__init__(self, parent)
        self.update_interval = update_interval
        self.parent = parent
        self.width = width
        self.height = height
        self.tk_image = None
        self.rec_on = cycle(x for x in xrange(rec_on))
        self.cross_on = False
        self.microscope = microscope
        self.cross_colour_index = cycle([(0, 0, 0), (255, 0, 0), 
            (0, 0, 255), (0, 255, 0), (255, 255, 255)])
        self.cross_colour = next(self.cross_colour_index)
        self.info_text = tk.Label(self, font="mono 12")
        self.info_text.grid(column=0, row=0)
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.grid(column=0, row=1)
        self.canvas.bind("<ButtonPress-1>", self.start_slide)
        self.canvas.bind("<B1-Motion>", self.slide_image)
        hscrollbar = tk.Scrollbar(self)
        hscrollbar.grid(column=1, row=1, sticky=tk.N+tk.S+tk.E)
        self.canvas.config(yscrollcommand=hscrollbar.set)
        hscrollbar.config(command=self.canvas.yview)
        vscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        vscrollbar.grid(column=0, row=2, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=vscrollbar.set)
        vscrollbar.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.microscope.recording_start_stop, ()),
             ("INTPOL", 5, self.microscope.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("S+", 2, self.microscope.series_up_down, (1,)),
             ("S-", 2, self.microscope.series_up_down, (-1,)),
             ("T+", 2, self.microscope.set_time_interval, (
              self.microscope.series_time_interval,)),
             ("T-", 2, self.microscope.set_time_interval, (
              -self.microscope.series_time_interval,)),
             ("ON", 2, self.set_cross_on_off, ()),
             ("C", 2, self.change_cross_colour, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            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:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                image, "raw", "BGR")
            if self.cross_on:
                ImageDraw.Draw(tk_image).text((width / 2, height / 2), "+", 
                    self.cross_colour,ImageFont.truetype("FreeSans.ttf", 30))
        except RuntimeError:
            self.raise_cam_id_error()
            return
        self.canvas.delete("img")
        self.tk_image = ImageTk.PhotoImage(tk_image)
        self.canvas.create_image((self.width / 2,self.height / 2), 
            anchor=tk.CENTER, image=self.tk_image, tag="img")

        rec_text = "#" if self.microscope.recording \
            and next(self.rec_on) == 0 else " "
        self.info_text.config(text="REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3} RES:{4}:{5} > {6}"
            .format(
            rec_text,
            self.microscope.interpolation,
            self.microscope.number_of_imgs,                            
            self.microscope.series_time_interval,
            width, height,
            self.microscope.aspect_ratio))
 
        series_state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
        for button in (1, 2, 3, 6, 7, 8, 9, 10, 11, 14):
            self.buttons[button].config(state=series_state)
               
        rec_state = tk.DISABLED if self.microscope.recording else tk.NORMAL
        for button in (1, 2, 3, 6, 14):
            self.buttons[button].config(state=rec_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_cross_colour(self):
        self.cross_colour = next(self.cross_colour_index)
        
    def set_cross_on_off(self):
        self.cross_on = not self.cross_on
        self.buttons[12].config(text="OFF" if self.cross_on else "ON")
        
    def raise_cam_id_error(self):
        self.canvas.delete("img", "rec")
        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 release(self):
        self.parent.destroy()

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    root.resizable(0, 0)
    
    try:
        with Microscope(series_time_interval=1) as microscope:
            microscope_ui = MicroscopeUI(
                root, microscope, WIDTH, HEIGHT, 200, 3)
            microscope_ui.pack()
            microscope_ui.run()
            root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
            root.bind("<Key" + chr(10) + ">", microscope.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()
Gruß Frank
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kaytec: Ich dachte Du wolltest die Werte als Konstanten auf der Klasse behalten? Ich hatte in der `__init__()`-Signatur deshalb eher ``update_interval=UPDATE_INTERVAL`` erwartet‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo __blackjack__,

es geht ja eigentlich auch so und "take_picture" war mal für einen Taster an dem USB-Mikroskop. Beim Betätigen des Schalters, gibt es eher Wackelbilder und somit unbrauchbar.

Gruß Frank
Antworten