arv*_*ndh 9 python webcam opencv tkinter python-multithreading
我一直在尝试创建一个tkinter顶级窗口,用于流式传输视频格式网络摄像头并进行QR扫描.我从SO获得了这个QR扫描代码,而另一个代码只是从网络摄像头更新图像而不是在tkinter标签上流式传输视频.
我试图将这两者结合起来,以便使用标签更新来自网络摄像头的图像的顶层窗口和关闭顶层窗口的关闭按钮.当它流式传输图像时,它可以扫描QR码,如果扫描成功,网络摄像头和顶层窗口将关闭.
这是我试过的.
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()
Run Code Online (Sandbox Code Playgroud)
问题是,
self.cam.release()或cv2.destroyAllWindows()我的网络摄像头灯或即使我强行终止程序编译).现在我得到的是一个由OpenCV创建的单独窗口,用于在内部传输视频.但我不想要那个窗口,而是希望视频显示在tkinter的顶层窗口上.当有一个成功的阅读时,网络摄像头卡在它读取的最终图像上.
我试图在类的run方法中删除负责OpenCV窗口的行BarcodeScanner
cv2.imshow(self.WINDOW_NAME, img[1])
Run Code Online (Sandbox Code Playgroud)
它仍然出现了一个没有输出的不同窗口,如果我试图关闭该窗口,它会创建另一个类似的递归.
更新:
正如我注意到我犯了一些愚蠢的错误而没有理解某些行cv2,我通过将顶层窗口代码添加到run类的方法中来对代码进行一些更改(我不确定这是否是正确的方法).
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()
Run Code Online (Sandbox Code Playgroud)
现在,我可以在Toplevel窗口上获取图像,但我不知道如何关闭网络摄像头.
条件1:当我显示要扫描的QR码时,它会成功读取并且网络摄像头退出而没有任何错误.
条件2:当我点击顶层窗口上的关闭按钮时(如果用户不想进行任何扫描而只想关闭网络摄像头)我得到错误说
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)
Run Code Online (Sandbox Code Playgroud)
我写这篇申请Linux,Mac和Windows机器.如何安全地关闭或终止网络摄像头.
您的程序有两个线程,主线程和从相机读取帧的工作线程。单击关闭按钮时,它发生在主线程中。self.cam.release()对象self.cam很可能处于不可用状态后,当self.cam工作线程调用 的某个方法时,可能会出现一些麻烦。也许 的实现cv2.VideoCapture是错误的,当发生这种情况时它应该抛出一些异常。
从主线程以外的其他线程访问 tkinter 小部件也可能会导致问题。
对于干净的程序终止,创建一个实例threading.Event,然后event.is_set()在工作线程中的某个点进行检查是可行的。例如
def destroy_video_window():
self.stop_event.set()
video_window.destroy()
Run Code Online (Sandbox Code Playgroud)
然后在工作线程中
while True:
if self.stop_event.is_set():
break
for i in range(0, self.CV_SYSTEM_CACHE_CNT):
self.cam.read()
Run Code Online (Sandbox Code Playgroud)
有几件事可以用其他方式完成,以下是代码的修改版本。它避免从主线程以外的其他线程调用 tkinter 方法,event_generate()这是工作线程调用的唯一 tkinter 方法。例如<<ScannerQuit>>,通过发出放置在 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()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3274 次 |
| 最近记录: |