【发布时间】:2016-09-02 12:31:11
【问题描述】:
我一直在尝试创建一个 tkinter 顶级窗口,用于从网络摄像头传输视频并进行 QR 扫描。我从 SO 和 another code 获得了这个 QR 扫描码,它只是更新来自网络摄像头的图像,而不是在 tkinter 标签上流式传输视频。
我尝试将这两者结合起来,以便一个带有标签的顶层窗口从网络摄像头更新图像,以及一个关闭按钮来关闭顶层窗口。在流式传输图像时,它可以扫描二维码,如果扫描成功,网络摄像头和顶层窗口将关闭。
这是我尝试过的。
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()
问题是,
- 我实际上想在 Toplevel 窗口上显示视频,而不是 OpenCV 窗口
- 同时进行 QR 扫描,如果读取成功,顶层窗口应该关闭而不会突然关闭网络摄像头(因为,当我尝试使用
self.cam.release()或cv2.destroyAllWindows()时,我的网络摄像头灯或即使我强制终止程序编译)。
现在我得到的是由 OpenCV 创建的一个单独的窗口,可以在其中流式传输视频。但我不想要那个窗口,而是希望视频显示在 tkinter 的顶层窗口上。同样,当读取成功时,网络摄像头会停留在它读取的最终图像上。
我试图在BarcodeScanner 类的run 方法中删除负责OpenCV 窗口的行
cv2.imshow(self.WINDOW_NAME, img[1])
它仍然显示一个没有输出的不同窗口,如果我尝试关闭该窗口,它会创建另一个类似的递归窗口。
更新:
当我注意到我在不了解 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()
现在,我可以在 Toplevel 窗口中获取图像,但我不知道如何关闭网络摄像头。
条件 1: 当我显示二维码扫描时,它读取成功并且网络摄像头没有任何错误退出。
条件 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)
我正在为Linux、Mac 和Windows 机器编写此应用程序。如何安全地关闭或终止网络摄像头。
【问题讨论】:
-
@tfv ,.它看起来相似,但不一样。在那里,视频使用函数显示在窗口上,但在这里,由于我同时在显示视频的同时进行 QR 扫描,因此使用类和线程将它们组合起来变得更加困难
标签: python opencv tkinter webcam python-multithreading