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,

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.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 = self.INTERPOLATION_METHODS
        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 np.array_equal(self.last_frame, frame):
            self.release()
            state = False
            
        self.last_frame = frame
        
        if state:        
            inter_frame = cv2.resize(frame, self.zoom_size, 
            interpolation = self.interpolation_methods[
            self.interpolation_method])
            
            image = Image.frombytes("RGB",  self.zoom_size, inter_frame,
                "raw", "BGR")
            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 +1, 
                            self.img_format)
                        self.get_image().save(os.path.join(
                            self.series_img_path, self.series_dir, img_name))
                    else:
                        self.take_series = False
            if self.recording:
                self.video_writer.write(inter_frame)
                    
            return image
        else:
            return state

    def recording_start_stop(self, name = None):
        if not name:
            name = "{0:%d%b%Y_%H_%M_%S.%f}.avi".format(
                datetime.datetime.utcnow())
        if self.recording:
            self.recording = False
        else:
            self.recording = True
            self.video_writer = cv2.VideoWriter(os.path.join(
                self.video_path, name), cv2.cv.CV_FOURCC(* VIDEO_CODEC),
                24, self.zoom_size)
                
    def take_series_picture(self):
        if self.take_series == False:         
            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)
        self.get_image().save(os.path.join(self.image_path, name))
        
    def series_up_down(self, step):
        if self.take_series == False: 
            if self.number_of_imgs > step *-1 or step > 0:
                self.number_of_imgs += step
        
    def set_time_interval(self, step):
        if self.take_series == False:
            if self.series_time_interval > step *-1 or step > 0:
                self.series_time_interval += step
        
    def zoom_image(self, step):
        width = int(self.zoom_width + step * self.zoom_width / self.zoom_height)
        height = int(self.zoom_height + step)
        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.recording = False
        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)
        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.take_picture, ()),
             ("REC", 2, self.recording_film, ()),
             ("INTPOL", 5, self.next_interpolation_method, ()),
             ("[S]]]", 2, self.take_series_pictures, ()),
             ("S+", 2, self.series_up_down, (5,)),
             ("S-", 2, self.series_up_down, (-5,)),
             ("T+", 2, self.time_interval_up_down, (5,)),
             ("T-", 2, self.time_interval_up_down, (-5,)),
             ("ON", 2, self.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 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):
        tk_image = self.microscope.get_image()
        if tk_image:
            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.recording:
                if self.rec_on.next() == 0:
                    self.canvas.create_oval(10, 10, 25, 25, fill="red", 
                        tag="rec")
            if self.text_on:
                self.canvas.create_text(self.width / 2, self.height / 2, 
                    text="+", font="Arial 30", fill=self.text_colour, 
                    tag="txt")
                self.canvas.create_text(30, 10, font="Arial 11 bold", 
                text = "FILTER: {0}  SERIES: {1}  TIME: {2}  RES: {3}".format(
                    self.microscope.interpolation, 
                    self.microscope.number_of_imgs,                             
                    self.microscope.series_time_interval,
                    self.microscope.zoom_size), anchor="nw", 
                    fill = self.text_colour, tag = "txt")
            
            for button in (1, 2, 3, 7, 8, 9, 10, 11):
                if self.microscope.take_series:
                    self.buttons[button].config(state = tk.DISABLED)
                else:
                    self.buttons[button].config(state = tk.NORMAL)
                
                    
            width, height = self.microscope.zoom_size
            self.canvas.config(scrollregion = (0, 0, width, height))
            self.after_id = self.after(self.UPDATE_INTERVAL, self.run)
        else:
            self.raise_cam_id_error()
            
    def change_text_colour(self):       
        self.text_on_off(True)
        self.text_colour = self.text_colour_index.next()
        
    def text_on_off(self, state = False):
        if state or not self.text_on:
            self.text_on = True
        else:
            self.text_on = state
            
        self.buttons[12].config(text="ON" if self.text_on else "OFF")
        
    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 series_up_down(self, step):
        self.text_on_off(True)
        self.microscope.series_up_down(step)
        
    def time_interval_up_down(self, step):
        self.text_on_off(True)
        self.microscope.set_time_interval(step)
        
    def next_interpolation_method(self):
        self.text_on_off(True)
        self.microscope.next_interpolation_method()
        
    def recording_film(self):
        if self.recording:
            self.microscope.recording_start_stop()
            self.recording = False
        else:
            self.microscope.recording_start_stop()
            self.recording = True
            
    def take_series_pictures(self):
        self.microscope.take_series_picture()

    def take_picture(self):
        self.microscope.take_picture()

    def release(self):
        self.parent.destroy()

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    
    try:
        with Microscope(series_time_interval=5) 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
snafu
User
Beiträge: 6736
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Willst du nicht langsam ein Repo anlegen? Das wäre übersichtlicher. Du könntest z.B. was auf GitHub Gist hochladen.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Zeile 49: warum `INTERPOLATION_METHODS` in `interpolation_methods` umbenennen?
Zeile 110: das setzen des Namens ist nur im else-Teil nötig.
Zeile 122: auf False prüft man nicht explizit sondern mit not
Zeile 138: »step * -1« ist das selbe wie »-step«, die zweite Bedingung ist in der ersten schon enthalten.
Zeile 147: durch mehrfaches Zoomen wird »width« durch Rundungsfehler immer ungenauer. Berechne »width« über die Aspect-Ratio immer aus »height«.
Zeile 246: solche bedingten Parameter macht man entweder direkt mit if-else oder mit Variablen:

Code: Alles auswählen

            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)
Zeile 267: das ist unschön, einmal den expliziten Wert und einmal eine Variable zu nehmen. Was gefällt dir an

Code: Alles auswählen

self.text_on = state or not self.text_on
nicht?
Zeile 291: zu viel Wiederholungen in den beiden Blöcken. Geht auch ohne if.

Und jetzt nochmal zum einzigen wirklichen Fehler: »get_image« sollte nur einen Typ an Rückgabewert haben, nicht ZWEI. Wenn ein Bild nicht genommen werden kann, sollte eine Exception geworfen werden.

Code: Alles auswählen

    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
        inter_frame = cv2.resize(frame, self.zoom_size,
        interpolation = self.interpolation_methods[
            self.interpolation_method])
        
        image = Image.frombytes("RGB",  self.zoom_size, inter_frame,
            "raw", "BGR")
        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)
                    self.get_image().save(os.path.join(
                        self.series_img_path, self.series_dir, img_name))
                else:
                    self.take_series = False
        if self.recording:
            self.video_writer.write(inter_frame)
        return image
Ein paar Zählfehler waren auch noch drin.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo,

habe aus dem Modul noch Pil rausgenommen - macht das irgendwie Sinn ? Es gibt bei OpenCV irgendwo auch die Möglichkeit des Arrayvergleiches, doch kann ich irgendwie nicht finden ?

Das habe ich mal so gemacht - so ganz verstanden habe ich es nicht.

Code: Alles auswählen

    def recording_film(self):
        self.recording = False or not self.recording
        self.microscope.recording_start_stop()

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 self.take_series == False: 
            if self.number_of_imgs > step *-1:
                self.number_of_imgs += step
        
    def set_time_interval(self, step):
        if self.take_series == False:
            if self.series_time_interval > step *-1:
                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.recording = False
        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)
        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.take_picture, ()),
             ("REC", 2, self.recording_film, ()),
             ("INTPOL", 5, self.next_interpolation_method, ()),
             ("[S]]]", 2, self.take_series_pictures, ()),
             ("S+", 2, self.series_up_down, (1,)),
             ("S-", 2, self.series_up_down, (-1,)),
             ("T+", 2, self.time_interval_up_down, (1,)),
             ("T-", 2, self.time_interval_up_down, (-1,)),
             ("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 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:
            tk_image = None
            
        if tk_image:
            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.recording:
                if self.rec_on.next() == 0:
                    self.canvas.create_oval(10, 10, 25, 25, fill="red", 
                        tag="rec")
            if self.text_on:
                self.canvas.create_text(self.width / 2, self.height / 2, 
                    text="+", font="Arial 30", fill=self.text_colour, 
                    tag="txt")
                self.canvas.create_text(30, 10, font="Arial 11 bold", 
                text = "FILTER: {0}  SERIES: {1}  TIME: {2}  RES: {3}".format(
                    self.microscope.interpolation, 
                    self.microscope.number_of_imgs,                             
                    self.microscope.series_time_interval,
                    self.microscope.zoom_size), anchor="nw", 
                    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)
        else:
            self.raise_cam_id_error()
            
    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 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 recording_film(self):
        self.recording = False or not self.recording
        self.microscope.recording_start_stop()
            
    def take_series_pictures(self):
        self.microscope.take_series_picture()

    def take_picture(self):
        self.microscope.take_picture()

    def release(self):
        self.parent.destroy()

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    
    try:
        with Microscope(series_time_interval=5) 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
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

self.recording = False or not self.recording

Kannst du zusammenkürzen zu

self.recording = not self.recording

Also einfach nur jeweils das Gegenteil von vorher. Das noch mit “or False” zu verknüpfen ist sinnfrei, das ändert nie das Ergebnis.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Ob man jetzt die if-Abfrage in Zeile 110 wirklich in den Aufruf einbauen muß?
Zeile 130: nicht explizit auf False prüfen sondern not benutzen.
Zeile 131: step * -1 ist -step

Zeile 187ff: `take_picture`, `take_series_pictures` und `recording_film` können auch direkt von microscope genommen werden und brauchen keine eigenen Methoden in MicroscopeUI. Wenn man das text-on wegläßt auch noch einige andere.
Zeile 221: Exceptions sind dazu da, dass man direkt eine Fehlerbehandlung machen kann und nicht um ein Flag zu setzen, um dann wieder mit Flags zu arbeiten.
Zeile 228/282: Bei recording benutzt Du ein redundantes Flag in MicroscopeUI bei take_series das von Microscope.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo __deets__ u. Siriuis3,

danke für eure Kritik und ich werde weitermachenl.

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

Hallo,

Die Fehlerbehandlung habe ich so abgeschaltet, da die GUI es anzeigen soll. So können meine Nutzer (Kinder) darauf reagieren und es selbst neu starten . Die Kinder sollen mich eigentlich in Ruhe lassen, denn ich möchte meine Zeit mit Kaffee trinken verbringen und überall meine Erziehersstempel verteilen. Wie könnte ich es sonst lösen ?

Mit dem step*-1 habe ich irgendwann explizit geprüft das der Schritt positiv ist . Dies war bei dem Wert 1 nötig, denn da sollt er bei -step keinen Schritt mehr nach unten machen, sondern nur hoch laufen. Jetzt geht es auch so ? Seit Programmierer über diesen Code hergefallen sind, verstehe ich ihn selbst nicht mehr.

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.recording = False
        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)
        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.microscope.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 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:
            tk_image = None
            
        if tk_image:
            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.microscope.recording:
                if not self.rec_on.next():
                    self.canvas.create_oval(10, 10, 25, 25, fill="red", 
                        tag="rec")
            if self.text_on:
                width, height = self.microscope.zoom_size
                self.canvas.create_text(width / 2, height / 2, 
                    text="+", font="Arial 30", fill=self.text_colour, 
                    tag="txt")
                self.canvas.create_text(width / 2, 
                    10 + height/2 - self.height / 2, 
                    font="Arial 11 bold", 
                    text = "FILTER: {0}  SERIES: {1}  TIME: {2}  RES: {3}"
                    .format(self.microscope.interpolation, 
                    self.microscope.number_of_imgs,                             
                    self.microscope.series_time_interval,
                    self.microscope.zoom_size), 
                    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)
        else:
            self.raise_cam_id_error()
            
    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 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()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich verstehe nicht, was Du uns mit Deinen einleitenden Worten sagen willst. Ich habe versucht, den Code sauberer zu schreiben, dass sowohl Du ihn jetzt als auch in zwei Jahren noch verstehst.

Der Code sieht jetzt ganz in Ordnung aus, bis auf `run`:

Code: Alles auswählen

    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")
        if self.microscope.recording:
            if not self.rec_on.next():
                self.canvas.create_oval(10, 10, 25, 25, fill="red", tag="rec")
        if self.text_on:
            width, height = self.microscope.zoom_size
            self.canvas.create_text(width / 2, height / 2,
                text="+", font="Arial 30", fill=self.text_colour,
                tag="txt")
            self.canvas.create_text(width / 2,
                10 + height/2 - self.height / 2,
                font="Arial 11 bold",
                text = "FILTER: {0}  SERIES: {1}  TIME: {2}  RES: {3}"
                .format(self.microscope.interpolation,
                    self.microscope.number_of_imgs,                            
                    self.microscope.series_time_interval,
                    self.microscope.zoom_size),
                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)
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo Sirius3,
die Einleitung sollte nicht viel Tiefe haben, sondern eher meine Unfähigkeit zeigen. Eure Hilfe ist toll und der Lerneffekt ist für mich natürlich super, doch habe ich natürlich auch große Pause beim Programmieren und irgendwie alles Gelernte wieder vergessen. Da tauchen öfters die gleichen Fehler und Verständnisfragen auf. Ich benutze das Programm wirklich auf meiner Arbeit und versuche es anwenderfreundlicher zu machen, damit die Kinder selbstständig arbeiten können. Werde dafür einige Funktionen wieder abschalten und 2-3 Buttons behalten. Es wird solche Software zu kaufen geben und bestimmt auch in einer freien Version, doch so kann ich sie verändern und anpassen. Bei Vorschulkindern würde ein Button reichen.

Ist es eigentlich OK den veränderten Code komplett einzufügen ?

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.recording = False
        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)
        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.microscope.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 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")
        if self.microscope.recording:
            if not self.rec_on.next():
                self.canvas.create_oval(10, 10, 25, 25, fill="red", tag="rec")
        if self.text_on:
            width, height = self.microscope.zoom_size
            self.canvas.create_text(width / 2, height / 2,
                text="+", font="Arial 30", fill=self.text_colour,
                tag="txt")
            self.canvas.create_text(width / 2,
                10 + height/2 - self.height / 2,
                font="Arial 11 bold",
                text = "FILTER: {0}  SERIES: {1}  TIME: {2}  RES: {3}"
                .format(self.microscope.interpolation,
                    self.microscope.number_of_imgs,                            
                    self.microscope.series_time_interval,
                    self.microscope.zoom_size),
                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 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ß und Dank
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: 17737
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: 13064
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‽
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
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: 13064
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. :-)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten