gpt4 book ai didi

python:tkinter 显示来自网络摄像头的视频并进行 QR 扫描

转载 作者:太空狗 更新时间:2023-10-29 20:31:17 26 4
gpt4 key购买 nike

我一直在尝试创建一个 tkinter 顶级窗口,该窗口可以从网络摄像头流式传输视频并进行 QR 扫描。我从SO得到这个二维码和 another code它只是更新来自网络摄像头的图像,而不是在 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()

问题是,

  1. 我实际上想在 Toplevel 窗口而不是 OpenCV 窗口上显示视频
  2. 同时进行 QR 扫描,如果读取成功,Toplevel 窗口应该关闭而不会突然关闭网络摄像头(因为,当我尝试使用 self.cam.release()cv2.destroyAllWindows() 即使我强行终止程序编译,我的网络摄像头也会亮起或打开)。

现在我得到的是一个由 OpenCV 创建的单独窗口,它在内部流式传输视频。但我不想要那个窗口,而是我希望视频显示在 tkinter 的顶层窗口上。此外,当读取成功时,网络摄像头会停留在它读取的最终图像上。

我试图删除负责 OpenCV 窗口的行,在 BarcodeScanner 类的 run 方法中

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:当我显示要扫描的 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)

我正在为 LinuxMacWindows 机器编写此应用程序。如何安全地关闭或终止网络摄像头。

最佳答案

您的程序有两个线程,主线程和从相机读取帧的工作线程。单击关闭按钮时,它发生在主线程中。在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()

然后在工作线程中

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

有几件事可以用其他方式完成,以下是代码的修改版本。它避免从主线程以外的其他线程调用 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()

关于python:tkinter 显示来自网络摄像头的视频并进行 QR 扫描,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37089228/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com