2016-05-07 17 views
9

Tôi đã cố gắng tạo một cửa sổ cấp cao nhất để phát trực tuyến video dạng webcam và thực hiện quét QR. Tôi nhận mã quét QR này từ SOanother code chỉ cập nhật hình ảnh từ webcam thay vì phát trực tuyến video trên nhãn tkinter.python: tkinter để hiển thị video từ webcam và thực hiện quét QR

và tôi đã cố kết hợp cả hai cửa sổ này để cửa sổ to hơn với nhãn cập nhật hình ảnh từ webcam và nút đóng để đóng cửa sổ toplevel. Và trong khi nó truyền hình ảnh, nó có thể quét mã QR và nếu quét thành công, webcam và cửa sổ toplevel bị đóng.

đây là những gì tôi đã thử.

import cv2 
import cv2.cv as cv 
import numpy 
import zbar 
import time 
import threading 
import Tkinter 
from PIL import Image, ImageTk 

class BarCodeScanner(threading.Thread, Tkinter.Toplevel): 
    def __init__(self): 
     # i made this as a global variable so i can access this image 
     # outside ie,. beyond the thread to update the image on to the tkinter window 
     global imgtk 
     imgtk = None 
     threading.Thread.__init__(self) 
     self.WINDOW_NAME = 'Camera' 
     self.CV_SYSTEM_CACHE_CNT = 5 # Cv has 5-frame cache 
     self.LOOP_INTERVAL_TIME = 0.2 
     cv.NamedWindow(self.WINDOW_NAME, cv.CV_WINDOW_NORMAL) 
     self.cam = cv2.VideoCapture(-1) 
     self.confirm = 0 

    def scan(self, aframe): 
     imgray = cv2.cvtColor(aframe, cv2.COLOR_BGR2GRAY) 
     # to show coloured image, as from the other code mentioned in the other code 
     imgcol = cv2.cvtColor(aframe, cv2.COLOR_BGR2RGBA) 
     imgcol_array = Image.fromarray(imgcol) 
     imgtk = ImageTk.PhotoImage(image=imgcol_array) 

     raw = str(imgray.data) 
     scanner = zbar.ImageScanner() 
     scanner.parse_config('enable') 
     width = int(self.cam.get(cv.CV_CAP_PROP_FRAME_WIDTH)) 
     height = int(self.cam.get(cv.CV_CAP_PROP_FRAME_HEIGHT)) 
     imageZbar = zbar.Image(width, height,'Y800', raw) 
     scanner.scan(imageZbar) 

     for symbol in imageZbar: 
      print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data 
      return symbol.data 

    def run(self): 
     self.datalst = [] 
     print 'BarCodeScanner run', time.time() 
     while True:     
      for i in range(0,self.CV_SYSTEM_CACHE_CNT): 
       self.cam.read() 
      img = self.cam.read() 
      self.data = self.scan(img[1]) 

      cv2.imshow(self.WINDOW_NAME, img[1]) 
      cv.WaitKey(1) 
      time.sleep(self.LOOP_INTERVAL_TIME) 
      if self.data: 
       self.datalst.append(self.data) 
      # i have added this section so that it waits for scan 
      # if a scan is made it and if gets same value after 2 scans 
      # it has to stop webcam 
      if len(self.datalst) == 2 and len(set(self.datalst)) <= 1: 
       # I want to close the webcam before closing the toplevel window 
       #self.cam.release() 
       #cv2.destroyAllWindows() 
       break 
     self.cam.release() 

def Video_Window(): 
    video_window = Tkinter.Toplevel() 
    video_window.title('QR Scan !!') 
    img_label = Tkinter.Label(video_window) 
    img_label.pack(side=Tkinter.TOP) 
    close_button = Tkinter.Button(video_window, text='close', command = video_window.destroy) 
    close_button.pack(side=Tkinter.TOP) 

    def update_frame(): 
     global imgtk 
     img_label.configure(image=imgtk) 
     img_label.after(10,update_frame) 
    update_frame() 

def main(): 
    root = Tkinter.Tk() 
    button_scanQr = Tkinter.Button(root, text='QR Scan', command=start_scan) 
    button_scanQr.pack() 
    root.mainloop() 

def start_scan(): 
    scanner = BarCodeScanner() 
    scanner.start() 

    Video_Window() 
    #scanner.join() 

main() 

Vấn đề là,

  1. Tôi thực sự muốn để hiển thị video trên cửa sổ mục cấp đầu, không phải là cửa sổ OpenCV
  2. đồng thời làm một quét QR, nếu một đọc là thành công, cửa sổ Toplevel sẽ đóng mà không đột ngột đóng webcam (vì, khi tôi cố gắng sử dụng self.cam.release() hoặc cv2.destroyAllWindows() đèn webcam của mình hoặc bật ngay cả khi tôi buộc chấm dứt chương trình biên dịch).

Bây giờ, những gì tôi nhận được là một cửa sổ riêng được tạo bởi OpenCV để truyền video bên trong. Nhưng tôi không muốn cửa sổ đó, thay vào đó tôi muốn video được hiển thị trên cửa sổ tokivel của tkinter. cũng có khi đọc thành công, webcam sẽ dừng lại ở hình ảnh cuối cùng mà nó đọc.

tôi đã cố gắng để loại bỏ các dòng đó là chịu trách nhiệm cho cửa sổ OpenCV, bên trong lớp run phương pháp BarcodeScanner

cv2.imshow(self.WINDOW_NAME, img[1]) 

nó vẫn xuất hiện với một cửa sổ khác nhau với không có đầu ra, và nếu tôi cố gắng để đóng mà cửa sổ, nó tạo ra một cái khác tương tự và đệ quy.

CẬP NHẬT:

Như tôi nhận thấy tôi thực hiện một số sai lầm ngớ ngẩn mà không hiểu biết về một số dòng trong cv2, tôi thực hiện một số thay đổi về mã bằng cách thêm mã cửa sổ mục cấp đầu vào phương pháp run của lớp (im không chắc chắn nếu đây là một cách đúng).

import cv2 
import cv2.cv as cv 
import numpy 
import zbar 
import time 
import threading 
import Tkinter 
from multiprocessing import Process, Queue 
from Queue import Empty 
from PIL import Image, ImageTk 

class BarCodeScanner(threading.Thread, Tkinter.Toplevel): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     #self.WINDOW_NAME = 'Camera' 
     self.CV_SYSTEM_CACHE_CNT = 5 # Cv has 5-frame cache 
     self.LOOP_INTERVAL_TIME = 0.2 
     #cv.NamedWindow(self.WINDOW_NAME, cv.CV_WINDOW_NORMAL) 
     self.cam = cv2.VideoCapture(-1) 
     # check if webcam device is free 
     self.proceede = self.cam.isOpened() 
     if not self.proceede: 
      return 
     self.confirm = 0 

    def scan(self, aframe): 
     imgray = cv2.cvtColor(aframe, cv2.COLOR_BGR2GRAY) 
     raw = str(imgray.data) 
     scanner = zbar.ImageScanner() 
     scanner.parse_config('enable')   
     width = int(self.cam.get(cv.CV_CAP_PROP_FRAME_WIDTH)) 
     height = int(self.cam.get(cv.CV_CAP_PROP_FRAME_HEIGHT)) 
     imageZbar = zbar.Image(width, height,'Y800', raw) 
     scanner.scan(imageZbar) 
     for symbol in imageZbar: 
      print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data 
      return symbol.data 

    def run(self): 
     if not self.proceede: 
      return 
     def show_frame(): 
      _, img = self.cam.read() 
      img = cv2.flip(img,1) 
      cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA) 
      img = Image.fromarray(cv2image) 
      imgtk = ImageTk.PhotoImage(image=img) 
      img_label.imgtk = imgtk 
      img_label.configure(image=imgtk) 
      video_window.after(250, show_frame) 

     def destroy_video_window(): 
      self.cam.release() 
      video_window.destroy() 

     # Toplevel GUI 
     video_window = Tkinter.Toplevel() 
     video_window.title('QR Scan !!') 
     img_label = Tkinter.Label(video_window) 
     img_label.pack(side=Tkinter.TOP) 
     close_button = Tkinter.Button(video_window, text='close', command = destroy_video_window) 
     close_button.pack(side=Tkinter.RIGHT) 
     show_frame() 

     self.datalst = [] 
     print 'BarCodeScanner run', time.time() 
     while True: 
      for i in range(0,self.CV_SYSTEM_CACHE_CNT): 
       self.cam.read() 
      img = self.cam.read() 
      self.data = self.scan(img[1]) 
      time.sleep(self.LOOP_INTERVAL_TIME) 
      if self.data: 
       self.datalst.append(self.data) 
      if len(self.datalst) == 2 and len(set(self.datalst)) <= 1: 
       video_window.destroy() 
       break 
     self.cam.release() 

def main(): 
    root = Tkinter.Tk() 
    button_scanQr = Tkinter.Button(root, text='QR Scan', command=scaner) 
    button_scanQr.pack() 
    root.mainloop() 

def scaner(): 
    scanner = BarCodeScanner() 
    scanner.start() 

main() 

bây giờ, tôi có thể lấy hình ảnh trên cửa sổ Toplevel, Nhưng tôi không biết cách đóng webcam.

điều kiện 1: khi tôi hiển thị mã QR để quét, nó đọc thành công và thoát webcam mà không gặp bất kỳ lỗi nào.

điều kiện 2: khi tôi nhấp vào nút Close trên cửa sổ mục cấp đầu (nói nếu người dùng không muốn làm bất cứ quét và chỉ muốn đóng webcam) tôi nhận được lỗi nói

libv4l2: error dequeuing buf: Invalid argument 
VIDIOC_DQBUF: Invalid argument 
select: Bad file descriptor 
VIDIOC_DQBUF: Bad file descriptor 
select: Bad file descriptor 
VIDIOC_DQBUF: Bad file descriptor 
Segmentation fault (core dumped) 

Tôi đang viết ứng dụng này cho Linux, MacWindows máy. Làm thế nào tôi có thể đóng hoặc chấm dứt webcam một cách an toàn.

+0

có thể trùng lặp của [Sử dụng OpenCV với Tkinter] (http://stackoverflow.com/questions/32342935/using-opencv-with-tkinter) – tfv

+0

@tfv,. nó trông tương tự, nhưng không giống nhau. ở đó video được hiển thị trên cửa sổ bằng cách sử dụng chức năng, nhưng ở đây kể từ khi im thực hiện quét QR đồng thời cùng với hiển thị video, kết hợp chúng bằng cách sử dụng lớp và luồng làm cho khó khăn hơn nhiều – arvindh

Trả lời

1

Chương trình của bạn có hai luồng, chủ đề chính và chuỗi công nhân đọc khung từ máy ảnh. Khi nút đóng được nhấp, nó sẽ xảy ra trong chuỗi chính. Sau self.cam.release() đối tượng self.cam có thể ở trạng thái không sử dụng được và khi phương thức self.cam được gọi bởi chuỗi công nhân, có thể có một số sự cố. Có lẽ việc thực hiện cv2.VideoCapture là bị lỗi và nó sẽ ném một số ngoại lệ khi điều đó xảy ra.

Truy cập các tiện ích tkinter từ chủ đề khác ngoài chuỗi chính cũng có thể gây ra sự cố.

Để chấm dứt chương trình sạch, tạo một phiên bản threading.Event và sau đó kiểm tra event.is_set() tại một số thời điểm trong chuỗi công việc có thể hoạt động. Ví dụ

def destroy_video_window(): 
    self.stop_event.set() 
    video_window.destroy() 

và sau đó trong các sợi nhân

while True: 
    if self.stop_event.is_set(): 
     break 
    for i in range(0, self.CV_SYSTEM_CACHE_CNT): 
     self.cam.read() 

Có một vài điều mà có thể được thực hiện theo cách khác, sau đây là một phiên bản sửa đổi của mã này. Nó tránh các phương thức gọi tkinter từ chủ đề khác ngoài luồng chính, event_generate() là phương thức tkinter duy nhất được gọi bởi luồng công nhân. Tránh bỏ phiếu rõ ràng bằng cách phát ra các sự kiện ảo, ví dụ: <<ScannerQuit>>, được đặt trong hàng đợi sự kiện tkinter.

import cv2 
import cv2.cv as cv 
import zbar 
import time 
import threading 
import Tkinter as tk 

from PIL import Image, ImageTk 

class Scanner(object): 
    def __init__(self, handler, *args, **kw): 
     self.thread = threading.Thread(target=self.run) 
     self.handler = handler 

     self.CV_SYSTEM_CACHE_CNT = 5 # Cv has 5-frame cache 
     self.LOOP_INTERVAL_TIME = 0.2 
     self.cam = cv2.VideoCapture(-1) 

     self.scanner = zbar.ImageScanner() 
     self.scanner.parse_config('enable') 
     self.cam_width = int(self.cam.get(cv.CV_CAP_PROP_FRAME_WIDTH)) 
     self.cam_height = int(self.cam.get(cv.CV_CAP_PROP_FRAME_HEIGHT)) 

     self.last_symbol = None 

    def start(self): 
     self.thread.start() 

    def scan(self, aframe): 
     imgray = cv2.cvtColor(aframe, cv2.COLOR_BGR2GRAY) 
     raw = str(imgray.data) 
     image_zbar = zbar.Image(self.cam_width, self.cam_height, 'Y800', raw) 
     self.scanner.scan(image_zbar) 

     for symbol in image_zbar: 
      return symbol.data 

    def run(self): 
     print 'starting scanner' 

     while True: 
      if self.handler.need_stop(): 
       break 

      # explanation for this in 
      # http://stackoverflow.com/a/35283646/5781248 
      for i in range(0, self.CV_SYSTEM_CACHE_CNT): 
       self.cam.read() 

      img = self.cam.read() 

      self.handler.send_frame(img) 

      self.data = self.scan(img[1]) 

      if self.handler.need_stop(): 
       break 

      if self.data is not None and (self.last_symbol is None 
              or self.last_symbol <> self.data): 
       # print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data 
       self.handler.send_symbol(self.data) 
       self.last_symbol = self.data 

      time.sleep(self.LOOP_INTERVAL_TIME) 

     self.cam.release() 

class ScanWindow(tk.Toplevel): 
    def __init__(self, parent, gui, *args, **kw): 
     tk.Toplevel.__init__(self, master=parent, *args, **kw) 

     self.parent = parent 
     self.gui = gui 
     self.scanner = None 

     self.lock = threading.Lock() 
     self.stop_event = threading.Event() 

     self.img_label = tk.Label(self) 
     self.img_label.pack(side=tk.TOP) 

     self.close_button = tk.Button(self, text='close', command=self._stop) 
     self.close_button.pack() 

     self.bind('<Escape>', self._stop) 

     parent.bind('<<ScannerFrame>>', self.on_frame) 
     parent.bind('<<ScannerEnd>>', self.quit) 
     parent.bind('<<ScannerSymbol>>', self.on_symbol) 

    def start(self): 
     self.frames = [] 
     self.symbols = [] 

     class Handler(object): 
      def need_stop(self_): 
       return self.stop_event.is_set() 

      def send_frame(self_, frame): 
       self.lock.acquire(True) 
       self.frames.append(frame) 
       self.lock.release() 

       self.parent.event_generate('<<ScannerFrame>>', when='tail') 

      def send_symbol(self_, data): 
       self.lock.acquire(True) 
       self.symbols.append(data) 
       self.lock.release() 

       self.parent.event_generate('<<ScannerSymbol>>', when='tail') 

     self.stop_event.clear() 
     self.scanner = Scanner(Handler()) 
     self.scanner.start() 
     self.deiconify() 

    def _stop(self, *args): 
     self.gui.stop() 

    def stop(self): 
     if self.scanner is None: 
      return 

     self.stop_event.set() 

     self.frames = [] 
     self.symbols = [] 
     self.scanner = None 
     self.iconify() 

    def quit(self, *args): 
     self.parent.event_generate('<<ScannerQuit>>', when='tail') 

    def on_symbol(self, *args): 
     self.lock.acquire(True) 
     symbol_data = self.symbols.pop(0) 
     self.lock.release() 

     print 'symbol', '"%s"' % symbol_data 
     self.after(500, self.quit) 

    def on_frame(self, *args): 
     self.lock.acquire(True) 
     frame = self.frames.pop(0) 
     self.lock.release() 

     _, img = frame 
     img = cv2.flip(img, 1) 
     cv2image = cv2.cvtColor(img, cv2.COLOR_BGR2RGBA) 
     img = Image.fromarray(cv2image) 
     imgtk = ImageTk.PhotoImage(image=img) 
     self.img_label.imgtk = imgtk 
     self.img_label.configure(image=imgtk) 

class GUI(object): 
    def __init__(self, root): 
     self.root = root 

     self.scan_window = ScanWindow(self.root, self) 
     self.scan_window.iconify() 

     self.root.title('QR Scan !!') 

     self.lframe = tk.Frame(self.root) 
     self.lframe.pack(side=tk.TOP) 

     self.start_button = tk.Button(self.lframe, text='start', command=self.start) 
     self.start_button.pack(side=tk.LEFT) 

     self.stop_button = tk.Button(self.lframe, text='stop', command=self.stop) 
     self.stop_button.configure(state='disabled') 
     self.stop_button.pack(side=tk.LEFT) 

     self.close_button = tk.Button(self.root, text='close', command=self.quit) 
     self.close_button.pack(side=tk.TOP) 

     self.root.bind('<<ScannerQuit>>', self.stop) 
     self.root.bind('<Control-s>', self.start) 
     self.root.bind('<Control-q>', self.quit) 
     self.root.protocol('WM_DELETE_WINDOW', self.quit) 

    def start(self, *args): 
     self.start_button.configure(state='disabled') 
     self.scan_window.start() 
     self.stop_button.configure(state='active') 

    def stop(self, *args): 
     self.scan_window.stop() 
     self.start_button.configure(state='active') 
     self.stop_button.configure(state='disabled') 

    def quit(self, *args): 
     self.scan_window.stop() 
     self.root.destroy() 

def main(): 
    root = tk.Tk() 
    gui = GUI(root) 
    root.mainloop() 

main() 
Các vấn đề liên quan