2017-04-03 4 views
1

Ich versuche, eine GUI zum Abspielen eines Videos zu erstellen, die den gesamten Bildschirm füllt, während die Schaltfläche für Snapshot immer noch am unteren Rand sichtbar ist. Im Moment, was ich zu tun habe, ist nur das App-Fenster selbst auf Vollbild einzustellen, was zu einem kleinen Video oben und einem riesigen "Schnappschuss" -Knopf am Button führt. Gibt es eine Möglichkeit, das Video den gesamten Bildschirm ausfüllen zu lassen?zeigt Video auf dem gesamten Bildschirm mit OpenCV und Tkiner

danke!

from PIL import Image, ImageTk 
import Tkinter as tk 
import argparse 
import datetime 
import cv2 
import os 

class Application: 
    def __init__(self, output_path = "./"): 
     """ Initialize application which uses OpenCV + Tkinter. It displays 
      a video stream in a Tkinter window and stores current snapshot on disk """ 
     self.vs = cv2.VideoCapture('Cat Walking.mp4') # capture video frames, 0 is your default video camera 
     self.output_path = output_path # store output path 
     self.current_image = None # current image from the camera 

     self.root = tk.Tk() # initialize root window 
     self.root.title("PyImageSearch PhotoBooth") # set window title 
     # self.destructor function gets fired when the window is closed 
     self.root.protocol('WM_DELETE_WINDOW', self.destructor) 

     self.panel = tk.Label(self.root) # initialize image panel 
     self.panel.pack(padx=10, pady=10) 

     # create a button, that when pressed, will take the current frame and save it to file 
     btn = tk.Button(self.root, text="Snapshot!", command=self.take_snapshot) 
     btn.pack(fill="both", expand=True, padx=10, pady=10) 

     # start a self.video_loop that constantly pools the video sensor 
     # for the most recently read frame 
     self.video_loop() 


    def video_loop(self): 
     """ Get frame from the video stream and show it in Tkinter """ 
     ok, frame = self.vs.read() # read frame from video stream 
     if ok: # frame captured without any errors 
      cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA 
      self.current_image = Image.fromarray(cv2image) # convert image for PIL 
      imgtk = ImageTk.PhotoImage(image=self.current_image) # convert image for tkinter 
      self.panel.imgtk = imgtk # anchor imgtk so it does not be deleted by garbage-collector 
      self.root.attributes("-fullscreen",True) 
      #self.oot.wm_state('zoomed') 
      self.panel.config(image=imgtk) # show the image 

     self.root.after(1, self.video_loop) # call the same function after 30 milliseconds 

    def take_snapshot(self): 
     """ Take snapshot and save it to the file """ 
     ts = datetime.datetime.now() # grab the current timestamp 
     filename = "{}.jpg".format(ts.strftime("%Y-%m-%d_%H-%M-%S")) # construct filename 
     p = os.path.join(self.output_path, filename) # construct output path 
     self.current_image.save(p, "JPEG") # save image as jpeg file 
     print("[INFO] saved {}".format(filename)) 

    def destructor(self): 
     """ Destroy the root object and release all resources """ 
     print("[INFO] closing...") 
     self.root.destroy() 
     self.vs.release() # release web camera 
     cv2.destroyAllWindows() # it is not mandatory in this application 

# construct the argument parse and parse the arguments 
ap = argparse.ArgumentParser() 
ap.add_argument("-o", "--output", default="./", 
    help="path to output directory to store snapshots (default: current folder") 
args = vars(ap.parse_args()) 

# start the app 
print("[INFO] starting...") 
pba = Application(args["output"]) 
pba.root.mainloop() 

Antwort

1

Es ist keine schwierige Aufgabe, wenn Sie nicht über die Ausführungszeit kümmern! Wir wussten, dass die Größenanpassung eines Bildes kein Hexenwerk für den normalen Benutzer ist, aber unter der Haube braucht es einige Zeit, um jeden Frame zu skalieren. Und wenn Sie sich wirklich über Zeit und Möglichkeiten wundern - es gibt viele Möglichkeiten, von numpy/scipy bis skimage/skvideo zu spielen.

Aber lassen Sie uns versuchen, etwas mit Ihrem Code "wie es ist" zu tun, so haben wir zwei Optionen zu spielen: cv2 und Image. Für den Test nahm ich 20 Sekunden von "Keyboard Cat" Video von youtube (480p) und jeden Frame bis zu 1080p skalieren und GUI sieht wie folgt aus (Vollbild 1920x1080):

enter image description here

Resize Methoden/timeit verstrichene Zeit von zeigt Frames:

Wie Sie sehen - kein großer Unterschied zwischen diesem Fall entweder zwei so ist hier ein Code (nur Application Klasse und video_loop geändert):

#imports 
try: 
    import tkinter as tk 
except: 
    import Tkinter as tk 
from PIL import Image, ImageTk 
import argparse 
import datetime 
import cv2 
import os 


class Application: 
    def __init__(self, output_path = "./"): 
     """ Initialize application which uses OpenCV + Tkinter. It displays 
      a video stream in a Tkinter window and stores current snapshot on disk """ 
     self.vs = cv2.VideoCapture('KeyCat.mp4') # capture video frames, 0 is your default video camera 
     self.output_path = output_path # store output path 
     self.current_image = None # current image from the camera 

     self.root = tk.Tk() # initialize root window 
     self.root.title("PyImageSearch PhotoBooth") # set window title 

     # self.destructor function gets fired when the window is closed 
     self.root.protocol('WM_DELETE_WINDOW', self.destructor) 
     self.root.attributes("-fullscreen", True) 

     # getting size to resize! 30 - space for button 
     self.size = (self.root.winfo_screenwidth(), self.root.winfo_screenheight() - 30) 

     self.panel = tk.Label(self.root) # initialize image panel 
     self.panel.pack(fill='both', expand=True) 

     # create a button, that when pressed, will take the current frame and save it to file 
     self.btn = tk.Button(self.root, text="Snapshot!", command=self.take_snapshot) 
     self.btn.pack(fill='x', expand=True) 

     # start a self.video_loop that constantly pools the video sensor 
     # for the most recently read frame 
     self.video_loop() 

    def video_loop(self): 
     """ Get frame from the video stream and show it in Tkinter """ 
     ok, frame = self.vs.read() # read frame from video stream 
     if ok: # frame captured without any errors 
      cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA 
      cv2image = cv2.resize(cv2image, self.size, interpolation=cv2.INTER_NEAREST) 
      self.current_image = Image.fromarray(cv2image) #.resize(self.size, resample=Image.NEAREST) # convert image for PIL 
      self.panel.imgtk = ImageTk.PhotoImage(image=self.current_image) 
      self.panel.config(image=self.panel.imgtk) # show the image 

      self.root.after(1, self.video_loop) # call the same function after 30 milliseconds 

Aber man wusste - tun, eine solche Dinge „auf fly“ isn‘ t eine gute Idee, können so versuchen alle Frames zuerst, um die Größe und führen Sie dann alle Sachen (nur Application Klasse und video_loop Methode geändert, resize_video Methode ergänzt):

class Application: 
    def __init__(self, output_path = "./"): 
     """ Initialize application which uses OpenCV + Tkinter. It displays 
      a video stream in a Tkinter window and stores current snapshot on disk """ 
     self.vs = cv2.VideoCapture('KeyCat.mp4') # capture video frames, 0 is your default video camera 
     ... 
     # init frames 
     self.frames = self.resize_video() 
     self.video_loop() 

def resize_video(self): 
    temp = list() 
    try: 
     temp_count_const = cv2.CAP_PROP_FRAME_COUNT 
    except AttributeError: 
     temp_count_const = cv2.cv.CV_CAP_PROP_FRAME_COUNT 

    frames_count = self.vs.get(temp_count_const) 

    while self.vs.isOpened(): 
     ok, frame = self.vs.read() # read frame from video stream 
     if ok: # frame captured without any errors 
      cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA 
      cv2image = cv2.resize(cv2image, self.size, interpolation=cv2.INTER_NEAREST) 
      cv2image = Image.fromarray(cv2image) # convert image for PIL 
      temp.append(cv2image) 
      # simple progress print w/o sys import 
      print('%d/%d\t%d%%' % (len(temp), frames_count, ((len(temp)/frames_count)*100))) 
     else: 
      return temp 

def video_loop(self): 
    """ Get frame from the video stream and show it in Tkinter """ 
    if len(self.frames) != 0: 
     self.current_image = self.frames.pop(0) 
     self.panel.imgtk = ImageTk.PhotoImage(self.current_image) 
     self.panel.config(image=self.panel.imgtk) 
     self.root.after(1, self.video_loop) # call the same function after 30 milliseconds 

timeit verstrichene Zeit des Anzeigens von vordimensionierten Bildern: ~ 78,78 s.

Wie Sie sehen - Größenanpassung ist kein Hauptproblem Ihres Skripts, aber eine gute Option!

+0

Hey CommonSense, danke für die tolle Antwort. Wenn ich Code versuche, bekomme ich cv2image = cv2.resize (cv2image, self.size, interpolation = cv2.INTER_NEAREST) ​​ Fehler: C: \ Benutzer \ David \ Downloads \ opencv-master \ opencv-master \ module \ core \ src \ alloc.cpp: 52: error: (-4) Fehler beim Zuordnen von 9191424 Bytes in der Funktion cv :: OutOfMemoryError, keine Idee warum? (ich verwende 32 Bit Python) – David

+0

Wie viel RAM hast du und was ist deine Fullscreen-Auflösung und Länge eines Clips? Anyway, OpenCV ist sehr empfindlich auf Plattform, also habe ich versucht, Code auf 64-Bit-Win/64-Bit-Python/8 GB RAM und habe keinen Fehler mit 20 Sekunden Clip! Aber was ist mit Größenänderung mit 'Image' Bibliothek? Hast Du es versucht? Kommentieren Sie 'cv2image = cv2.resize (...)' line und rplace 'cv2image = Image.fromarray (cv2image)' mit 'cv2image = Image.fromarray (cv2image) .resize (self.size, resample = Image.NEAREST) '. – CommonSense

+0

CommonSense, mehr oder weniger gleich, wenn ich 17% Prozess erreiche, bekomme ich den Mem-Fehler. Ich bin auf einem ziemlich starken PC, i7 7700k + 32gb Ram auf Win10 64. Ich musste 32-Bit-Python verwenden, weil ich wahrscheinlich die openCV/openCV Contrib-Module in 32bit gebaut.Du denkst das ist mein Problem? Ich kann versuchen, openCV für 64bit – David

Verwandte Themen