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

Danke __blackjack__ .

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8
from __future__ import division

import Tkinter as tk
import os
import datetime
import cv2
from PIL import Image, ImageTk, ImageDraw, ImageFont
from functools import partial
from itertools import cycle
import numpy as np

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


class Microscope(object):

    PROPID_WIDTH = 3
    PROPID_HEIGHT = 4
    INTERPOLATION_METHODS = {"NEAREST" : cv2.INTER_NEAREST,
                             "LINEAR" : cv2.INTER_LINEAR,
                             "AREA" : cv2.INTER_AREA,
                             "CUBIC" : cv2.INTER_CUBIC,
                             "LANCZOS4" : cv2.INTER_LANCZOS4}
                            
    ASPECT_RATIO = {
        '{}/{}'.format(a, b): b / a
        for a, b in [
            (3, 1),
            (3, 2),
            (4, 3),
            (5, 3),
            (8, 3),
            (9, 5),
            (14, 9),
            (16, 10),
            (16, 9),
            (19, 10),
            (21, 9),
            (25, 12),
            (25, 16),
            (43, 18),
        ]
    }
    def __init__(self, cam_id=DEFAULT_CAM_ID, number_of_imgs=10, 
        series_time_interval=1, image_path=".", video_path=".",
        series_img_path=".", img_format=".tiff"):
        self.cam = cv2.VideoCapture(cam_id)
        if not self.cam.isOpened():
            raise RuntimeError("can not open camera {0!r}".format(
                cam_id))
        self.img_format = img_format
        self.inter_frame = None
        self.last_frame = None
        self.take_series = False
        self.recording = False
        self.number_of_imgs = number_of_imgs
        self.series_counter = None
        self.series_time_counter = None
        self.series_time_interval = series_time_interval
        self.image_path = image_path
        self.video_path = video_path
        self.series_img_path = series_img_path
        self.series_dir = None
        self.interpolation_methods_keys = cycle(
            self.INTERPOLATION_METHODS.keys())
        self.interpolation_method = next(self.interpolation_methods_keys)
        self.cam_width = self.zoom_width = self.width = \
            int(self.cam.get(self.PROPID_WIDTH))
        self.cam_height = self.zoom_height = self.height = \
            int(self.cam.get(self.PROPID_HEIGHT))
        aspect_ratio = self.cam_height / self.cam_width
        if aspect_ratio not  in self.ASPECT_RATIO.values():
            self.ASPECT_RATIO["CAM"] = aspect_ratio
        self.aspect_ratio_keys = cycle(self.ASPECT_RATIO.keys())
        for i in xrange(len(self.ASPECT_RATIO)):
            self.aspect_ratio = next(self.aspect_ratio_keys)
            if self.ASPECT_RATIO[self.aspect_ratio] == aspect_ratio:
                break
        
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.release()
    
    @property
    def cam_size(self):
        return self.cam_width, self.cam_height
        
    @property
    def size(self):
        return self.width, self.height
        
    @property
    def zoom_size(self):
        return self.zoom_width, self.zoom_height
        
    @property
    def interpolation(self):
        return self.interpolation_method

    def get_image(self):
        state, frame = self.cam.read()
       
        if state and np.array_equal(self.last_frame, frame):
            self.release()
            state = False
        if not state:
            raise RuntimeError("could not read image")
        
        self.crop_frame = cv2.resize(frame, self.zoom_size,
            interpolation = self.INTERPOLATION_METHODS[
            self.interpolation_method])[int(self.zoom_height / 2 
            - self.zoom_width*self.ASPECT_RATIO[self.aspect_ratio] / 2) 
            : int(self.zoom_width * self.ASPECT_RATIO[self.aspect_ratio] 
            + self.zoom_height / 2 - int(self.zoom_width*self.ASPECT_RATIO[
            self.aspect_ratio]) / 2), 0 : self.zoom_width]
        self.height, self.width = self.crop_frame.shape[0], \
            self.crop_frame.shape[1]
            
        if self.take_series:
            self.series_time_counter += 1
            if self.series_time_counter > self.series_time_interval:
                self.series_counter += 1
                self.series_time_counter = 0
                if self.series_counter <= self.number_of_imgs:
                    img_name = "{0}{1}".format(self.series_counter,
                        self.img_format)
                    cv2.imwrite(os.path.join(self.series_img_path, 
                        self.series_dir, img_name), self.crop_frame)
                else:
                    self.take_series = False
                    
        if self.recording:
            self.video_writer.write(self.crop_frame)
        return self.crop_frame

    def recording_start_stop(self, name = None):
        if self.recording:
            self.recording = False
        else:
            self.recording = True
            self.video_writer = cv2.VideoWriter(os.path.join(
                self.video_path, name if name else 
                "{0:%d%b%Y_%H_%M_%S.%f}.avi".format(datetime.datetime.utcnow()
                )), cv2.VideoWriter_fourcc(* VIDEO_CODEC), 24, self.size)
                
    def take_series_picture(self):
        if not self.take_series:         
            self.series_dir = "{0:%d%b%Y_%H_%M_%S.%f}".format(
                datetime.datetime.utcnow())
            os.makedirs(self.series_dir)
            self.series_time_counter = 0
            self.series_counter = 0
            self.take_series = True

    def take_picture(self, name = None):
        if not name:
            name = "{0:%d%b%Y_%H_%M_%S.%f}{1}".format(
                datetime.datetime.utcnow(), self.img_format)
        cv2.imwrite(os.path.join(self.image_path, name), self.crop_frame)
        
    def series_up_down(self, step):
        if not self.take_series: 
            if self.number_of_imgs > -step:
                self.number_of_imgs += step
        
    def set_time_interval(self, step):
        if not self.take_series:
            if self.series_time_interval > -step:
                self.series_time_interval += step
        
    def zoom_image(self, step):
        height = int(self.zoom_height + step)
        width = int(height * self.cam_width / self.cam_height)
        if width > 0 and height > 0:
            self.zoom_width = width
            self.zoom_height = height

    def reset_zoom(self):
        self.zoom_width, self.zoom_height = self.cam_size
        
    def next_interpolation_method(self):
        self.interpolation_method = next(self.interpolation_methods_keys)
        
    def next_aspect_ratio(self):
        self.aspect_ratio = next(self.aspect_ratio_keys)
        
    def release(self):
        self.cam.release()
        
        
class MicroscopeUI(tk.Frame):
    
    def __init__(self, parent, microscope, width, height, update_interval=100,
            rec_on = 5, zoom_step=10):
        tk.Frame.__init__(self, parent)
        self.update_interval = update_interval
        self.parent = parent
        self.width = width
        self.height = height
        self.tk_image = None
        self.rec_on = cycle(x for x in xrange(rec_on))
        self.cross_on = False
        self.microscope = microscope
        self.cross_colour_index = cycle([(0, 0, 0), (255, 0, 0), 
            (0, 0, 255), (0, 255, 0), (255, 255, 255)])
        self.cross_colour = next(self.cross_colour_index)
        self.info_text = tk.Label(self, font="mono 12")
        self.info_text.grid(column=0, row=0)
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.grid(column=0, row=1)
        self.canvas.bind("<ButtonPress-1>", self.start_slide)
        self.canvas.bind("<B1-Motion>", self.slide_image)
        hscrollbar = tk.Scrollbar(self)
        hscrollbar.grid(column=1, row=1, sticky=tk.N+tk.S+tk.E)
        self.canvas.config(yscrollcommand=hscrollbar.set)
        hscrollbar.config(command=self.canvas.yview)
        vscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
        vscrollbar.grid(column=0, row=2, columnspan=5, sticky=tk.E+tk.W)
        self.canvas.config(xscrollcommand=vscrollbar.set)
        vscrollbar.config(command=self.canvas.xview)
        button_frame = tk.Frame(self)
        button_frame.grid(column=0, row=3, columnspan=2)
        self.buttons = list()
        for column, (text, width, command, var)in enumerate(
            (("||", 2, self.capture_start_stop, ()),
             ("Z+", 2, self.microscope.zoom_image, (zoom_step,)),
             ("Z-", 2, self.microscope.zoom_image, (-zoom_step,)),
             ("Z+/-", 2, self.microscope.reset_zoom, ()),
             ("[1]", 2, self.microscope.take_picture, ()),
             ("REC", 2, self.microscope.recording_start_stop, ()),
             ("INTPOL", 5, self.microscope.next_interpolation_method, ()),
             ("[S]]]", 2, self.microscope.take_series_picture, ()),
             ("S+", 2, self.microscope.series_up_down, (1,)),
             ("S-", 2, self.microscope.series_up_down, (-1,)),
             ("T+", 2, self.microscope.set_time_interval, (
              self.microscope.series_time_interval,)),
             ("T-", 2, self.microscope.set_time_interval, (
              -self.microscope.series_time_interval,)),
             ("ON", 2, self.set_cross_on_off, ()),
             ("C", 2, self.change_cross_colour, ()),
             ("AR", 2, self.microscope.next_aspect_ratio, ()))):
            button = tk.Button(button_frame, text=text, width=width,
                relief="raised", font="Arial 10 bold",
                command=partial(command, *var))
            button.grid(column=column, row=0)
            self.buttons.append(button)

    def slide_image(self, event):
        self.canvas.scan_dragto(event.x, event.y, gain=-1)
        
    def start_slide(self, event):
        self.canvas.scan_mark(event.x, event.y)
        
    def capture_start_stop(self):
        if self.after_id is None:
            self.buttons[0].config(text = "||")
            self.run()
        else:
            self.buttons[0].config(text = ">")
            self.after_cancel(self.after_id)
            self.after_id = None

    def run(self):
        try:
            image = self.microscope.get_image()
            width, height = self.microscope.size
            tk_image = Image.frombytes("RGB",  (width, height), 
                image, "raw", "BGR")
            if self.cross_on:
                ImageDraw.Draw(tk_image).text((width / 2, height / 2), "+", 
                    self.cross_colour,ImageFont.truetype("FreeSans.ttf", 30))
        except RuntimeError:
            self.raise_cam_id_error()
            return
        self.canvas.delete("img")
        self.tk_image = ImageTk.PhotoImage(tk_image)
        self.canvas.create_image((self.width / 2,self.height / 2), 
            anchor=tk.CENTER, image=self.tk_image, tag="img")

        rec_text = "#" if self.microscope.recording \
            and next(self.rec_on) == 0 else " "
        self.info_text.config(text="REC:{0}  FILTER:{1}  SERIES:{2}  TIME:{3} RES:{4}:{5} > {6}"
            .format(
            rec_text,
            self.microscope.interpolation,
            self.microscope.number_of_imgs,                            
            self.microscope.series_time_interval,
            width, height,
            self.microscope.aspect_ratio))
 
        series_state = tk.DISABLED if self.microscope.take_series else tk.NORMAL
        for button in (1, 2, 3, 6, 7, 8, 9, 10, 11, 14):
            self.buttons[button].config(state=series_state)
               
        rec_state = tk.DISABLED if self.microscope.recording else tk.NORMAL
        for button in (1, 2, 3, 6, 14):
            self.buttons[button].config(state=rec_state)
            
        width, height = self.microscope.zoom_size
        self.canvas.config(scrollregion = (0, 0, width, height))
        self.after_id = self.after(self.update_interval, self.run)
            
    def change_cross_colour(self):
        self.cross_colour = next(self.cross_colour_index)
        
    def set_cross_on_off(self):
        self.cross_on = not self.cross_on
        self.buttons[12].config(text="OFF" if self.cross_on else "ON")
        
    def raise_cam_id_error(self):
        self.canvas.delete("img", "rec")
        self.canvas.create_text((self.width / 2, self.height / 2),
            text='NO CAM', font='Arial 40')
        for button in self.buttons:
            button.config(state="disabled")

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

def main():
    root = tk.Tk()
    root.title('MICROSCOPE')
    root.resizable(0, 0)
    
    try:
        with Microscope(series_time_interval=1) as microscope:
            microscope_ui = MicroscopeUI(
                root, microscope, WIDTH, HEIGHT, 200, 3)
            microscope_ui.pack()
            microscope_ui.run()
            root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
            root.bind("<Key" + chr(10) + ">", microscope.take_picture)
            root.mainloop()
            
    except RuntimeError:
        tk.Label(root, text = 'can not open camera {0!r}'.format(
                DEFAULT_CAM_ID), font = "Arial 20", height = 10).pack()
        root.mainloop()
    
if __name__ == '__main__':
    main()
Gruß Frank
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kaytec: Ich dachte Du wolltest die Werte als Konstanten auf der Klasse behalten? Ich hatte in der `__init__()`-Signatur deshalb eher ``update_interval=UPDATE_INTERVAL`` erwartet‽
„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__,

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

Gruß Frank
Antworten