Mikroskop mit Tkinter-GUi

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

Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Dienstag 1. November 2016, 13:58

Hallo,
habe bei einem großen Auktionshaus ein USB-Mikroskop gekauft und es war nur eine Software für Windows dabei. Da sie aber so schön günstig war (20 €), wurde sie erworben und ich habe gehofft etwas mit OpenCV basteln zu können. Ist soweit recht brauchbar geworden und für meine Bedürfnisse ausreichend. Was muß so ein Ding noch können - Filter oder so ?

CAM STOP/STOP --> "||"
ZOOM + --> "+"
ZOOM - --> "-"
ZOOM NORMAL --> "+/-"
AUFNAHME FOTO --> "#"

Gruß Frank

  1. #! /usr/bin/env python
  2. # -*- coding: utf-8
  3. import Tkinter as tk
  4. import cv2
  5. from time import gmtime, strftime
  6. from PIL import Image, ImageTk
  7. from functools import partial
  8.  
  9. WIDTH = 640
  10. HEIGHT = 480
  11. PATH = "microscop/"
  12. FORMAT = "tiff"
  13. ZOOM_STEP = 10
  14.  
  15. class Microscope(object):
  16.    
  17.     PROPID_WIDTH = 3
  18.     PROPID_HEIGHT = 4
  19.    
  20.     def __init__(self, cam_id):
  21.         self.cam = cv2.VideoCapture(cam_id)
  22.         if self.cam.isOpened():
  23.             self.width = self.cam.get(self.PROPID_WIDTH)
  24.             self.height = self.cam.get(self.PROPID_HEIGHT)
  25.             self.aspect_ratio = self.width / self.height
  26.            
  27.     def get_image(self):
  28.         if self.cam.isOpened():
  29.             return Image.frombytes("RGB", (int(self.cam.get(
  30.                 self.PROPID_WIDTH)), int(self.cam.get(self.PROPID_HEIGHT))),
  31.                 self.cam.read()[1],"raw", "BGR").resize((self.width,
  32.                 self.height))
  33.         else:
  34.             return None
  35.        
  36.     def take_picture(self, path):
  37.         image = self.get_image().resize((self.width, self.height))
  38.         image.save(path)
  39.            
  40.     def zoom_image(self, step):
  41.         width = int(self.width + step * self.aspect_ratio)
  42.         height = int(self.height + step)
  43.        
  44.         if width > self.cam.get(self.PROPID_WIDTH)\
  45.             and height > self.cam.get(self.PROPID_HEIGHT)\
  46.             or height > self.cam.get(self.PROPID_HEIGHT) \
  47.             and width > self.cam.get(self.PROPID_WIDTH) :
  48.                 self.width = width
  49.                 self.height = height
  50.                
  51.     def reset_zoom(self):
  52.         self.width = self.cam.get(self.PROPID_WIDTH)
  53.         self.height = self.cam.get(self.PROPID_HEIGHT)
  54.        
  55.     def get_size(self):
  56.         return self.width, self.height
  57.                
  58.     def release(self):
  59.         self.cam.release()
  60.            
  61.  
  62. class MicroscopeUI(tk.Frame):
  63.    
  64.     UPDATE_INTERVAL = 10
  65.    
  66.     def __init__(self, parent, width, height, path, format, zoom_step):
  67.         tk.Frame.__init__(self, parent)
  68.         self.parent = parent
  69.         self.width = width
  70.         self.height = height
  71.         self.path = path
  72.         self.format = format
  73.         self.tk_image = None
  74.         self.microscope = Microscope(-1)
  75.         self.canvas = tk.Canvas(self, width=width, height=height)
  76.         self.canvas.grid(column=0, row=0)
  77.         vscrollbar = tk.Scrollbar(self)
  78.         vscrollbar.grid(column=1, row=0, sticky=tk.N+tk.S)
  79.         self.canvas.config(yscrollcommand=vscrollbar.set)
  80.         vscrollbar.config(command=self.canvas.yview)
  81.         hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
  82.         hscrollbar.grid(column=0, row=1, columnspan=5, sticky=tk.E+tk.W)
  83.         self.canvas.config(xscrollcommand=hscrollbar.set)
  84.         hscrollbar.config(command=self.canvas.xview)
  85.         self.buttons = list()
  86.         button_frame = tk.Frame(self)
  87.         button_frame.grid(column=0, row=3, columnspan=2)
  88.         for column, (text, command, var) in enumerate(
  89.             (("||", self.start_stop, None),
  90.              ("+", self.microscope.zoom_image, zoom_step),
  91.              ("-", self.microscope.zoom_image, -zoom_step),
  92.              ("+/-", self.microscope.reset_zoom, None),
  93.              ("#", self.take_picture, None))):
  94.             button = tk.Button(button_frame, text=text, width=4,
  95.                 relief="raised", font="Arial 10 bold")
  96.             button.grid(column=column, row=0)
  97.             self.buttons.append(button)
  98.             if var:
  99.                 button.config(command=partial(command, var))
  100.             else:
  101.                 button.config(command=command)
  102.        
  103.     def start_stop(self):
  104.         if self.after_id is None:
  105.             self.buttons[0].config(text = "||")
  106.             self.run()
  107.         else:
  108.             self.buttons[0].config(text = ">")
  109.             self.after_cancel(self.after_id)
  110.             self.after_id = None
  111.            
  112.     def run(self):
  113.         self.tk_image = self.microscope.get_image()
  114.         if self.tk_image:
  115.             self.canvas.delete("img")
  116.             self.tk_image = ImageTk.PhotoImage(self.tk_image)
  117.             self.canvas.create_image((0,0), anchor=tk.NW, image=self.tk_image,
  118.                 tag="img")
  119.             width, height = self.microscope.get_size()
  120.             self.canvas.config(scrollregion = (0, 0, width, height))
  121.             self.after_id = self.after(self.UPDATE_INTERVAL, self.run)
  122.         else:
  123.             self.canvas.create_text((self.width / 2, self.height / 2),
  124.                 text='NO CAM', font='Arial 40')
  125.             for button in self.buttons:
  126.                 button.config(state="disabled")
  127.        
  128.     def take_picture(self, event = None):
  129.         self.microscope.take_picture("{0}{1}.{2}".format(self.path, strftime(
  130.             "%d%b%Y_%H_%M_%S", gmtime()), self.format))
  131.        
  132.     def release(self):
  133.         self.microscope.release()
  134.         self.parent.destroy()
  135.        
  136. def main():
  137.     root = tk.Tk()
  138.     root.title('MICROSCOPE')
  139.     microscope_ui = MicroscopeUI(root, WIDTH, HEIGHT, PATH, FORMAT, ZOOM_STEP)
  140.     microscope_ui.pack(expand=tk.YES)
  141.     microscope_ui.run()
  142.     root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
  143.     root.mainloop()
  144. if __name__ == '__main__':
  145.     main()


Gruß Frank
BlackJack

Re: Mikroskop mit Tkinter-GUi

Beitragvon BlackJack » Sonntag 6. November 2016, 19:25

@kaytec: `Microscope` ist nicht robust. Wenn die Kamera nicht ”offen” ist, dann werden nicht alle Attribute definiert und damit gibt es in einigen Methoden `AttributeError`\s. Die `__init__()` sollte ein benutzbares Objekt hinterlassen. Also am besten eine Ausnahme auslösen wenn die Kamera nicht offen ist, denn dann ist das Objekt sowieso unbrauchbar.

Wenn es eine externe Ressource gibt, die man explizit wieder freigeben muss, dann bietet es sich an aus dem Objekt das diese Ressource kapselt einen „context manager“ zu machen, damit man ``with`` verwenden kann um sicherzustellen, dass die Ressource auch tatsächlich wieder freigegeben wird.

Ich gehe mal davon aus das sich die Höhe und Breite vom Kamerabild nicht ändert. Trotzdem wird das sehr oft im Code abgefragt. Sollte man es doch immer live abfragen müssen, würde ich da Properties draus machen, damit das nicht immer wieder im Quelltext stehen muss.

Beim `Image.frombytes()` hilft die Formatierung nicht unbedingt dabei zu sehen wo die Argumente anfangen und aufhören. Die Methode prüft nicht ob tatsächlich ein Bild gelesen wurde.

Die ``if``-Bedingung in der Zoom-Methode ist komisch. Sehe ich das falsch oder fragst Du da zweimal das gleiche ab, nur in umgekehrter Reihenfolge.

Ungetestet:
  1. class Microscope(object):
  2.    
  3.     PROPID_WIDTH = 3
  4.     PROPID_HEIGHT = 4
  5.    
  6.     def __init__(self, cam_id):
  7.         self.cam = cv2.VideoCapture(cam_id)
  8.         if not self.cam.isOpened():
  9.             raise RuntimeError('can not open camera {0!r}'.format(cam_id))
  10.         self.cam_width = int(self.cam.get(self.PROPID_WIDTH))
  11.         self.cam_height = int(self.cam.get(self.PROPID_HEIGHT))
  12.         self.width = self.height = None
  13.         self.reset_zoom()
  14.         self.aspect_ratio = self.width / self.height
  15.    
  16.     def __enter__(self):
  17.         return self
  18.  
  19.     def __exit__(self, *args):
  20.         self.release()
  21.  
  22.     @property
  23.     def size(self):
  24.         return (self.width, self.height)
  25.  
  26.     def get_image(self):
  27.         is_ok, image_data = self.cam.read()
  28.         if is_ok:
  29.             return Image.frombytes(
  30.                 'RGB',
  31.                 (self.cam_width, self.cam_height),
  32.                 image_data,
  33.                 'raw',
  34.                 'BGR'
  35.             ).resize(self.size)
  36.         else:
  37.             return None
  38.        
  39.     def take_picture(self, path):
  40.         self.get_image().resize(self.size).save(path)
  41.            
  42.     def zoom_image(self, step):
  43.         width = int(self.width + step * self.aspect_ratio)
  44.         height = int(self.height + step)
  45.         if width > self.cam_width and height > self.cam_height:
  46.             self.width = width
  47.             self.height = height
  48.                
  49.     def reset_zoom(self):
  50.         self.width = self.cam_width
  51.         self.height = self.cam_height
  52.                
  53.     def release(self):
  54.         self.cam.release()
Benutzeravatar
kaytec
User
Beiträge: 512
Registriert: Dienstag 13. Februar 2007, 21:57

Re: Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Mittwoch 9. November 2016, 03:02

Hallo BlackJack,

Bei mir hat es nie einen `AttributeError` geworfen - kann es auch gerade nicht mit Kamera testen, denn in meinem gebrauchten Leasingrückläufer haben schlaue Leute Kamera/Mikro/Bluetooth nicht eingebaut. Das mit dem benutzbaren Objekt ist jetzt für mich ziemlich egal aber bei gezeigtem Code sollte es klargestellt werden.

Den zweiten Absatz verstehe ich mal gar nicht.

Es gibt ja immer drei Bildgrössen (tatsächliches Kamerabild / das veränderte (Zoom) / Die Bildgrösse in der GUI --> wird dir klar sein) und daher frage ich sie bestimmt immer wieder ab - das Script ist schon älter und es hat einer die Doku vergessen. Das mit "@property" sieht echt immer gut aus, doch habe ich die Verwendung nie so verstanden. Die Methoden mit dem Unterstrich sind auch irgendwie schön, doch auch hier .... - was macht es ?

Ob ein Bild wirklich gelesen wird gibt die Abfrage nicht her --> war hier auch schon das Problem viewtopic.php?f=6&t=37926&p=291671#p291671

Die Abfrage in der "zoom_image" Methode beruhte aus dem Problem, dass bei dem Unterschreiten der tatsächlichen Bildgrösse immer ein step zu viel war und daher habe ich es so hingebastelt. Es wird bestimmt nach deiner Veränderung auch gehen.

Gruß und Dank Frank
BlackJack

Re: Mikroskop mit Tkinter-GUi

Beitragvon BlackJack » Montag 21. November 2016, 13:47

@kaytec: Wenn die Kamera nicht geöffnet ist, dann werden die Attribute `witdh`, `height`, und `aspect_ratio` nicht erstellt. `take_picture()`, `zoom_image()` und `get_size()` greifen darauf aber zu, das heisst die werden dann einen `AttributeError` auslösen.

Die Kamera ist eine externe Ressource die man wieder freigeben muss wenn man damit fertig ist. Python weiss nicht das man `realease()` aufrufen muss wenn das `Microscope`-Objekt nicht mehr benötigt wird. Um eine saubere Verwendung von solchen externen Ressourcen sicherzustellen hat Python die ``with``-Anweisung und das „context manager“-Protokoll das Datentypen verwenden um mit ``with`` zu interagieren. Das sind die `__enter__()`- und `__exit__()`-Methoden die beim betreten und verlassen eines ``with``-Blocks aufgerufen werden.
  1.       # ...
  2.       with Microscope(-1) as microscope:
  3.             microscope_ui = MicroscopeUI(
  4.                   root, microscope, WIDTH, HEIGHT, PATH, FORMAT, ZOOM_STEP
  5.             )
  6.             # ...
  7.             root.mainloop()

Solange sich das tatsächliche Kamerabild in der grösse nicht ändert, braucht man es nicht jedes mal abfragen. Und falls das doch der Fall sein sollte, dann würde man den Code dafür nicht in jede Methode schreiben, sondern Getter, beziehungsweise in Python dann eher Properties.

Wenn das Flag sich nicht an die Dokumentation hält sollte man es trotzdem nicht ignorieren. Es kann ja trotzdem noch Fälle geben wo tatsächlich `False` zurückgegeben wird. Und vielleicht fixt ja mal irgendwann jemand den Fehler in OpenCV bzw. der Python-Anbindung.

Bei der Zoom-Bedingung habe ich ja nichts an der Bedingung verändert, also ich habe nicht eine vorhandene Bedingung irgendwie anders formuliert, sondern nur den sehr offensichtlich redundanten Teil weg gelassen. Und dann jetzt aber nachgefragt ob das denn überhaupt so sein sollte, oder ob Du da nicht vielleicht einen Fehler gemacht hast. Also das irgendwo width vielleicht height heissen sollte, eben so das da nicht zweimal das gleiche geprüft wird.
Benutzeravatar
kaytec
User
Beiträge: 512
Registriert: Dienstag 13. Februar 2007, 21:57

Re: Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Donnerstag 24. November 2016, 01:23

Hallo BlackJack,
danke für deine ausführlichen Erklärungen und falls ich de Zeit finde, werde ich versuchen es umzuschreiben.

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

Re: Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Freitag 28. Juli 2017, 11:18

Hallo,

nach langer Zeit mal wieder gebastelt und versucht die Verbesserungen einzubauen. Das Aufnehmen von Videos ist neu, doch wenn es in in Echtzeit sein soll, dann müsste die zeitliche Steuerung mit einem Nebenthread (sagt man glaub ich so ?) gelöst werden. Ich mache mit meine Tochter Aufnahmen von Uhrwerke, kleinen Lebewesen etc. Für solche Spielereien langt es und macht recht viel Spaß. Für die Steuerung der Aufnahme verwende ich den Farbstatus des Buttons und ist bestimmt eine richtige Bastelei !?
  1. #! /usr/bin/env python
  2. # -*- coding: utf-8
  3.  
  4. try:
  5.     import tkinter as tk
  6. except ImportError:
  7.     import Tkinter as tk
  8.  
  9. import time
  10. import datetime
  11. import cv2
  12. from PIL import Image, ImageTk
  13. from functools import partial
  14. WIDTH = 640
  15. HEIGHT = 480
  16.  
  17.  
  18. class Microscope(object):
  19.  
  20.     PROPID_WIDTH = 3
  21.     PROPID_HEIGHT = 4
  22.  
  23.     def __init__(self, cam_id = -1):
  24.         self.cam = cv2.VideoCapture(cam_id)
  25.         self.recording = False
  26.         if not self.cam.isOpened():
  27.             raise RuntimeError('can not open camera {0!r}'.format(
  28.                 cam_id))
  29.         self.cam_width = self.width = self.cam.get(self.PROPID_WIDTH)
  30.         self.cam_height = self.height = self.cam.get(self.PROPID_HEIGHT)
  31.         self.aspect_ratio = self.width / self.height
  32.  
  33.     def __enter__(self):
  34.         return self
  35.  
  36.     def __exit__(self, *args):
  37.         self.release()
  38.  
  39.     @property
  40.     def size(self):
  41.         return (self.width, self.height)
  42.  
  43.     def get_image(self):
  44.         state, frame = self.cam.read()
  45.         if state:
  46.             image = Image.frombytes("RGB", (int(self.cam_width),
  47.                 int(self.cam_height)) ,frame, "raw", "BGR"
  48.                     ).resize(self.size)
  49.             if self.recording:
  50.                 self.video_writer.write(frame)
  51.             return image
  52.         else:
  53.             return None
  54.  
  55.     def recording_start_stop(self, state, path = ""):
  56.         self.recording = state
  57.         if self.recording:
  58.             self.video_writer = cv2.VideoWriter(path, cv2.cv.CV_FOURCC(
  59.                 *"XVID"), 24, (int(self.cam_width),
  60.                 int(self.cam_height)))
  61.  
  62.     def take_picture(self, path):
  63.         image = self.get_image().resize((self.width, self.height))
  64.         image.save(path)
  65.  
  66.     def zoom_image(self, step):
  67.         width = int(self.width + step * self.aspect_ratio)
  68.         height = int(self.height + step)
  69.         if width > 0 and height > 0:
  70.             self.width = width
  71.             self.height = height
  72.  
  73.     def reset_zoom(self):
  74.         self.width, self.height = self.cam_width, self.cam_height
  75.  
  76.     def release(self):
  77.         self.cam.release()
  78.  
  79.  
  80. class MicroscopeUI(tk.Frame):
  81.  
  82.     UPDATE_INTERVAL = 10
  83.  
  84.     def __init__(self, parent, microscope, width, height,
  85.         zoom_step = 10, picture_path = "", video_path = ""):
  86.         tk.Frame.__init__(self, parent)
  87.         self.parent = parent
  88.         self.width = width
  89.         self.height = height
  90.         self.picture_path = picture_path
  91.         self.video_path = video_path
  92.         self.tk_image = None
  93.         self.buttons = list()
  94.         self.microscope = microscope
  95.         self.canvas = tk.Canvas(self, width=width, height=height)
  96.         self.canvas.grid(column=0, row=0)
  97.         vscrollbar = tk.Scrollbar(self)
  98.         vscrollbar.grid(column=1, row=0, sticky=tk.N+tk.S)
  99.         self.canvas.config(yscrollcommand=vscrollbar.set)
  100.         vscrollbar.config(command=self.canvas.yview)
  101.         hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
  102.         hscrollbar.grid(column=0, row=1, columnspan=5, sticky=tk.E+tk.W)
  103.         self.canvas.config(xscrollcommand=hscrollbar.set)
  104.         hscrollbar.config(command=self.canvas.xview)
  105.         button_frame = tk.Frame(self)
  106.         button_frame.grid(column=0, row=3, columnspan=2)
  107.         for column, (text, command, var)in enumerate(
  108.             (("||", self.start_stop, None),
  109.              ("+", self.microscope.zoom_image, zoom_step),
  110.              ("-", self.microscope.zoom_image, -zoom_step),
  111.              ("+/-", self.reset_zoom, None),
  112.              ("[ ]", self.take_picture, None),
  113.              ("REC", self.recording_film, None))):
  114.             button = tk.Button(button_frame, text=text, width=4,
  115.                 relief="raised", font="Arial 10 bold")
  116.             button.grid(column=column, row=0)
  117.             self.buttons.append(button)
  118.             if var:
  119.                  button.config(command=partial(command, var))
  120.             else:
  121.                  button.config(command=command)
  122.         self.buttons[-1].config(bg = "lightgreen")
  123.  
  124.     def start_stop(self):
  125.         if self.after_id is None:
  126.             self.buttons[0].config(text = "||")
  127.             self.run()
  128.         else:
  129.             self.buttons[0].config(text = ">")
  130.             self.after_cancel(self.after_id)
  131.             self.after_id = None
  132.  
  133.     def run(self):
  134.         self.tk_image = self.microscope.get_image()
  135.         if self.tk_image:
  136.             self.canvas.delete("img")
  137.             self.tk_image = ImageTk.PhotoImage(self.tk_image)
  138.             self.canvas.create_image((0,0), anchor=tk.NW,
  139.                 image=self.tk_image, tag="img")
  140.             width, height = self.microscope.size
  141.             self.canvas.config(scrollregion = (0, 0, width, height))
  142.             self.after_id = self.after(self.UPDATE_INTERVAL, self.run)
  143.         else:
  144.             self.canvas.create_text((self.width / 2, self.height / 2),
  145.                 text='NO CAM', font='Arial 40')
  146.             for button in self.buttons:
  147.                 button.config(state="disabled")
  148.  
  149.     def reset_zoom(self):
  150.         self.microscope.reset_zoom()
  151.  
  152.     def recording_film(self):
  153.         if self.buttons[-1].config("bg")[-1] == "lightgreen":
  154.             self.buttons[-1].config(bg = "red")
  155.             self.microscope.recording_start_stop(True,
  156.             "{0}{1:%d%b%Y_%H_%M_%S.%f}.avi".format(self.video_path,
  157.             datetime.datetime.utcnow()))
  158.         else:
  159.             self.buttons[-1].config(bg = "lightgreen")
  160.             self.microscope.recording_start_stop(False)
  161.  
  162.     def take_picture(self):
  163.         self.microscope.take_picture("{0}{1:%d%b%Y_%H_%M_%S.%f}.tiff"
  164.             .format(self.picture_path, datetime.datetime.utcnow()))
  165.  
  166.     def release(self):
  167.         self.parent.destroy()
  168.  
  169. def main():
  170.     root = tk.Tk()
  171.     root.title('MICROSCOPE')
  172.     with Microscope() as microscope:
  173.         microscope_ui = MicroscopeUI(
  174.             root, microscope, WIDTH, HEIGHT)
  175.         microscope_ui.pack(expand=tk.YES)
  176.         microscope_ui.run()
  177.         root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
  178.         root.mainloop()
  179. if __name__ == '__main__':
  180.     main()


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

Re: Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Freitag 9. Februar 2018, 11:43

Hallo,

wollte die Ausnahme in der Haupt-Gui verarbeiten, doch nehme ich den "mainloop" aus der "with" Anweisung, dann löst er auch bei einer Nichtauslösung des "except" Blockes, die "__exit__" Funktion aus und somit brauche ich einen zweiten "mainloop". Werden ja nur nach Bedarf gestartet, doch sollte es keine zwei geben !?

Gruß Frank
  1. def main():
  2.     root = tk.Tk()
  3.     root.title('MICROSCOPE')
  4.     try:
  5.         with Microscope(DEFAULT_CAM_ID) as microscope:
  6.             microscope_ui = MicroscopeUI(
  7.                 root, microscope, WIDTH, HEIGHT)
  8.             microscope_ui.pack(expand=tk.YES)
  9.             microscope_ui.run()
  10.             root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
  11.             root.mainloop()
  12.  
  13.     except RuntimeError:
  14.         tk.Label(root, text = 'can not open camera {0!r}'.format(
  15.                 DEFAULT_CAM_ID), font = "Arial 20", height = 10).pack()
  16.         root.mainloop()
  17.    
  18.  
  19. if __name__ == '__main__':
  20.     main()
Benutzeravatar
__deets__
User
Beiträge: 2141
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Mikroskop mit Tkinter-GUi

Beitragvon __deets__ » Freitag 9. Februar 2018, 12:12

Schön ist es nicht, eine wirklich bessere Lösung habe ich aber auch nicht. Aber das verbot der zwei Mainloops bezieht sich nicht auf die Anzahl der Aufrufe, sondern den Zeitpunkt. Insofern schon ok.
Benutzeravatar
kaytec
User
Beiträge: 512
Registriert: Dienstag 13. Februar 2007, 21:57

Re: Mikroskop mit Tkinter-GUi

Beitragvon kaytec » Sonntag 11. Februar 2018, 05:16

Hallo,

das Zoom hat nicht funktioniert und ich habe es umgebastelt und die Größe des Zooms auch zum "property" gemacht.

  1. #! /usr/bin/env python
  2. # -*- coding: utf-8
  3.  
  4. try:
  5.     import tkinter as tk
  6. except ImportError:
  7.     import Tkinter as tk
  8.  
  9. import time
  10. import datetime
  11. import cv2
  12. from PIL import Image, ImageTk
  13. from functools import partial
  14. WIDTH = 640
  15. HEIGHT = 480
  16. VIDEO_CODEC = "XVID"
  17. DEFAULT_CAM_ID = 1
  18.  
  19. class Microscope(object):
  20.  
  21.     PROPID_WIDTH = 3
  22.     PROPID_HEIGHT = 4
  23.  
  24.     def __init__(self, cam_id = id):
  25.         self.cam = cv2.VideoCapture(cam_id)
  26.         self.recording = False
  27.         if not self.cam.isOpened():
  28.             raise RuntimeError("can not open camera {0!r}".format(
  29.                 cam_id))
  30.         self.cam_width = self.zoom_width = self.cam.get(self.PROPID_WIDTH)
  31.         self.cam_height = self.zoom_height = self.cam.get(self.PROPID_HEIGHT)
  32.         self.aspect_ratio = self.zoom_width / self.zoom_height
  33.        
  34.  
  35.     def __enter__(self):
  36.         return self
  37.  
  38.     def __exit__(self, *args):
  39.         self.release()
  40.  
  41.     @property
  42.     def size(self):
  43.         return (int(self.cam_width), int(self.cam_height))
  44.        
  45.     @property
  46.     def zoom_size(self):
  47.         return (int(self.zoom_width), int(self.zoom_height))
  48.  
  49.     def get_image(self):
  50.         state, frame = self.cam.read()
  51.         if state:
  52.             image = Image.frombytes("RGB", self.size ,frame,
  53.                 "raw", "BGR").resize(self.zoom_size)
  54.             if self.recording:
  55.                 self.video_writer.write(frame)
  56.             return image
  57.         else:
  58.             return state
  59.  
  60.     def recording_start_stop(self, state, path = ""):
  61.         self.recording = state
  62.         if self.recording:
  63.             self.video_writer = cv2.VideoWriter(path, cv2.cv.CV_FOURCC(
  64.                 * VIDEO_CODEC), 24, (self.size))
  65.  
  66.     def take_picture(self, path):
  67.         self.get_image().save(path)
  68.  
  69.     def zoom_image(self, step):
  70.         width = int(self.zoom_width + step * self.aspect_ratio)
  71.         height = int(self.zoom_height + step)
  72.         if width > 0 and height > 0:
  73.             self.zoom_width = width
  74.             self.zoom_height = height
  75.  
  76.     def reset_zoom(self):
  77.         self.zoom_width, self.zoom_height = self.size
  78.  
  79.     def release(self):
  80.         self.cam.release()
  81.  
  82. class MicroscopeUI(tk.Frame):
  83.  
  84.     UPDATE_INTERVAL = 100
  85.  
  86.     def __init__(self, parent, microscope, width, height,
  87.         zoom_step = 10, picture_path = "", video_path = ""):
  88.         tk.Frame.__init__(self, parent)
  89.         self.parent = parent
  90.         self.width = width
  91.         self.height = height
  92.         self.picture_path = picture_path
  93.         self.video_path = video_path
  94.         self.tk_image = None
  95.         self.buttons = list()
  96.         self.microscope = microscope
  97.         self.canvas = tk.Canvas(self, width=width, height=height)
  98.         self.canvas.grid(column=0, row=0)
  99.         vscrollbar = tk.Scrollbar(self)
  100.         vscrollbar.grid(column=1, row=0, sticky=tk.N+tk.S)
  101.         self.canvas.config(yscrollcommand=vscrollbar.set)
  102.         vscrollbar.config(command=self.canvas.yview)
  103.         hscrollbar = tk.Scrollbar(self, orient=tk.HORIZONTAL)
  104.         hscrollbar.grid(column=0, row=1, columnspan=5, sticky=tk.E+tk.W)
  105.         self.canvas.config(xscrollcommand=hscrollbar.set)
  106.         hscrollbar.config(command=self.canvas.xview)
  107.         button_frame = tk.Frame(self)
  108.         button_frame.grid(column=0, row=3, columnspan=2)
  109.         for column, (text, command, var)in enumerate(
  110.             (("||", self.start_stop, None),
  111.              ("+", self.microscope.zoom_image, zoom_step),
  112.              ("-", self.microscope.zoom_image, -zoom_step),
  113.              ("+/-", self.reset_zoom, None),
  114.              ("[ ]", self.take_picture, None),
  115.              ("REC", self.recording_film, None))):
  116.             button = tk.Button(button_frame, text=text, width=4,
  117.                 relief="raised", font="Arial 10 bold")
  118.             button.grid(column=column, row=0)
  119.             self.buttons.append(button)
  120.             if var:
  121.                  button.config(command=partial(command, var))
  122.             else:
  123.                  button.config(command=command)
  124.         self.buttons[-1].config(bg = "lightgreen")
  125.  
  126.     def start_stop(self):
  127.         if self.after_id is None:
  128.             self.buttons[0].config(text = "||")
  129.             self.run()
  130.         else:
  131.             self.buttons[0].config(text = ">")
  132.             self.after_cancel(self.after_id)
  133.             self.after_id = None
  134.  
  135.     def run(self):
  136.         self.tk_image = self.microscope.get_image()
  137.         if self.tk_image:
  138.             self.canvas.delete("img")
  139.             self.tk_image = ImageTk.PhotoImage(self.tk_image)
  140.             self.canvas.create_image((0,0), anchor=tk.NW,
  141.                 image=self.tk_image, tag="img")
  142.             width, height = self.microscope.zoom_size
  143.             self.canvas.config(scrollregion = (0, 0, width, height))
  144.             self.after_id = self.after(self.UPDATE_INTERVAL, self.run)
  145.         else:
  146.             self.raise_cam_id_error()
  147.                
  148.     def raise_cam_id_error(self):
  149.         self.canvas.create_text((self.width / 2, self.height / 2),
  150.             text='NO CAM', font='Arial 40')
  151.         for button in self.buttons:
  152.             button.config(state="disabled")
  153.  
  154.     def reset_zoom(self):
  155.         self.microscope.reset_zoom()
  156.  
  157.     def recording_film(self):
  158.         if self.buttons[-1].config("bg")[-1] == "lightgreen":
  159.             self.buttons[-1].config(bg = "red")
  160.             self.microscope.recording_start_stop(True,
  161.             "{0}{1:%d%b%Y_%H_%M_%S.%f}.avi".format(self.video_path,
  162.             datetime.datetime.utcnow()))
  163.         else:
  164.             self.buttons[-1].config(bg = "lightgreen")
  165.             self.microscope.recording_start_stop(False)
  166.  
  167.     def take_picture(self):
  168.         self.microscope.take_picture("{0}{1:%d%b%Y_%H_%M_%S.%f}.tiff"
  169.             .format(self.picture_path, datetime.datetime.utcnow()))
  170.  
  171.     def release(self):
  172.         self.parent.destroy()
  173.  
  174. def main():
  175.     root = tk.Tk()
  176.     root.title('MICROSCOPE')
  177.     try:
  178.         with Microscope(DEFAULT_CAM_ID) as microscope:
  179.             microscope_ui = MicroscopeUI(
  180.                 root, microscope, WIDTH, HEIGHT)
  181.             microscope_ui.pack(expand=tk.YES)
  182.             microscope_ui.run()
  183.             root.protocol("WM_DELETE_WINDOW", microscope_ui.release)
  184.             root.mainloop()
  185.  
  186.     except RuntimeError:
  187.         tk.Label(root, text = 'can not open camera {0!r}'.format(
  188.                 DEFAULT_CAM_ID), font = "Arial 20", height = 10).pack()
  189.         root.mainloop()
  190.    
  191. if __name__ == '__main__':
  192.     main()


Gruß Frank

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder