Mikroskop mit Tkinter-GUi

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

Sonntag 15. April 2018, 20:47

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: 544
Registriert: Dienstag 13. Februar 2007, 21:57

Montag 23. April 2018, 19:39

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: 544
Registriert: Dienstag 13. Februar 2007, 21:57

Samstag 28. April 2018, 08:05

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: 7919
Registriert: Sonntag 21. Oktober 2012, 17:20

Samstag 28. April 2018, 17:49

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

Samstag 28. April 2018, 21:51

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
Antworten