Display webcam feed in Qt window - python

I'm a bit stuck here. I would like to display a webcam live feed in a PyQt5 window.
When i push the button the feed has to start, button turns green and text changes to "Stop camera" , on the next click the feed has to stop and the button has to revert to its original status and the feed is replaced with an image.
At the moment i only get a still image.
As soon as i get this working i would like to add some threading
Here is the code (updated):
import os
import threading
import timeit
import cv2
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import *
from PyQt5.uic import loadUi
class Worker1(QThread):
ImageUpdate = pyqtSignal(QImage)
def run(self):
self.ThreadActive = True
self.Capture = cv2.VideoCapture(0)
while self.ThreadActive:
ret, frame = self.Capture.read()
if ret:
Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
FlippedImage = cv2.flip(Image, 1)
ConvertToQtFormat = QImage(FlippedImage.data, FlippedImage.shape[1], FlippedImage.shape[0], QImage.Format_RGB888)
Pic = ConvertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.ImageUpdate.emit(Pic)
self.Capture.release()
cv2.destroyAllWindows()
def stop(self):
self.ThreadActive = False
self.terminate()
class FaceIdWindow(QtWidgets.QMainWindow):
def __init__(self):
super(FaceIdWindow, self).__init__()
self.ui = loadUi("uidesign/facereco/FaceId.ui", self)
self.ui.cmdChoosePicture.clicked.connect(self.ChoosePicture)
self.ui.cmdStartCamera.clicked.connect(self.StartCamera)
self.ui.cmdTrainFace.clicked.connect(self.TrainFace)
self.ui.cmdProcess.clicked.connect(self.Process)
self.status_camera = "STOPPED"
def StartCamera(self):
start = timeit.default_timer()
start = timeit.default_timer()
print("Start StartCamera\n")
print(self.status_camera)
if self.status_camera == "STOPPED":
self.status_camera = "STARTED"
self.ui.cmdStartCamera.setStyleSheet("background-color: green")
self.ui.cmdStartCamera.setText("Stop camera")
self.Worker1 = Worker1()
self.Worker1.start()
self.Worker1.ImageUpdate.connect(self.ImageUpdateSlot)
else:
self.status_camera = "STOPPED"
self.Worker1.stop()
image_path = str(os.getcwd())
image_path = image_path + "/assets/clipart/clipartfaceid2.png"
self.lblPicture.setPixmap(QtGui.QPixmap(image_path))
self.ui.cmdStartCamera.setStyleSheet("background-color: ")
self.ui.cmdStartCamera.setText("Start camera")
print("Stop StartCamera\n")
end = timeit.default_timer()
print("Process Time: ", (end - start))
def ImageUpdateSlot(self, Image):
self.lblPicture.setPixmap(QPixmap.fromImage(Image))
Any suggestions ?
Cheers , John

Seems i wasn't starting and stopping my thread in the right place.Code is updated , but if anyone has any improvements don't hesitate! Cheers John

Related

pyQT label rstp video streaming delay

My minimal working code snippet is below. When I read from rtsp IP camera which is in the same network with my computer now, I am getting delay around 1 second.
Is this because I am using python? this camera? or do you have a suggestion that If I am doing something wrong in the code
from PyQt5 import QtCore
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QSizePolicy, QLabel
from CalibrationGUI.qtgui.CameraThread import CaptureIpCameraFramesWorker
class VideoLabel(QLabel):
def __init__(self,camera_unit,ui_state_obj, parentGiven=None):
super(VideoLabel, self).__init__(parent=parentGiven)
self.ui_state_obj = ui_state_obj
self.camera_unit=camera_unit
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.setScaledContents(True)
self.installEventFilter(self)
self.setMaximumSize(1265536, 1265536)
self.setupUI()
def setupUI(self):
self.Camworker= CaptureIpCameraFramesWorker(self.camera_unit,self.ui_state_obj)
self.Camworker.ImageUpdated.connect(lambda image: self.ShowCamera(image))
#QtCore.pyqtSlot()
def ShowCamera(self, frame: QImage) -> None:
self.frame = frame
self.setPixmap(QPixmap.fromImage(frame))
def startStream(self):
self.Camworker.start()
def stopStream(self):
if self.Camworker.isRunning():
self.Camworker.quit()
def get_frame(self):
return self.Camworker.get_frame()
import cv2
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QImage
from PyQt5.QtCore import QThread, Qt
class CaptureIpCameraFramesWorker(QThread):
# Signal emitted when a new image or a new frame is ready.
ImageUpdated = pyqtSignal(QImage)
def __init__(self,camera_unit,UI_state_obj) -> None:
super(CaptureIpCameraFramesWorker, self).__init__()
# Declare and initialize instance variables
self.camera_unit = camera_unit
self.name = camera_unit.get_name()
self.__thread_active = True
self.fps = 0
self.__thread_pause = False
self.readframe=None
def get_frame(self):
return self.readframe
def run(self) -> None:
# While the thread is active.
while self.__thread_active:
if not self.__thread_pause:
# Grabs, decodes and returns the next video frame.
frame = self.camera_unit.get_current_image()
#=camera_unit.get_current_image gives image as numpy array and
#camera_unit is fetching image from link actively at the back end.
ret = frame is not None
if ret:
self.readframe=frame
# Get the frame height, width and channels.
height, width, channels = frame.shape
# Calculate the number of bytes per line.
bytes_per_line = width * channels
# If frame is read correctly.
# Convert image from BGR (cv2 default color format) to RGB (Qt default color format).
cv_rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Convert the image to Qt format.
qt_rgb_image = QImage(cv_rgb_image.data, width, height, bytes_per_line, QImage.Format_RGB888)
# Scale the image.
qt_rgb_image_scaled = qt_rgb_image.scaled(1280, 720, Qt.KeepAspectRatio) # 720p
self.ImageUpdated.emit(qt_rgb_image_scaled)
# When everything done, release the video capture object.
# cap.release()
# Tells the thread's event loop to exit with return code 0 (success).
self.quit()
I have modified the code in https://github.com/god233012yamil/Streaming-IP-Cameras-Using-PyQt-and-OpenCV/blob/main/Streaming_IP_Camera_Using_PyQt_OpenCV.py#L150

Changing QThread variable from a sub window

I am writing a WebCam Gui, which is supposed to take pictures and manipulate with WebCam parameters. After the video stream is activated on the main GUI window, an additional window can be opened to change the WebCamera parameters Screenshot 1, Screenshot 2.
I am using Qthread to stream on QLabel. Also, I was able to set the initial camera parameters on the camera properties' changing window. My problem is changing the Exposure parameter by using a slider on the sub-window and seeing results in real-time on the main window.
Please see the code.
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import cv2
class MainFrame(QMainWindow):
def __init__(self):
super(MainFrame, self).__init__()
# Loading UI
uic.loadUi("MainFrame.ui", self)
# Remove maximize button to prevent crushing
self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint)
# Define Widgets
self.Video_Feed = self.findChild(QLabel, 'Video_Feed')
self.Case_Name = self.findChild(QLineEdit, "Case_Name")
self.Pictures_List = self.findChild(QListWidget, "Pictures_List")
self.Start_Video = self.findChild(QAction, 'actionStart_Video')
self.Start_Video.setShortcut('Shift+S')
self.Stop_Video = self.findChild(QAction, 'actionStop_Video')
self.Stop_Video.setShortcut('Shift+F')
self.Take_a_Picture = self.findChild(QAction, 'actionTake_a_picture')
self.Take_a_Timed_Picture = self.findChild(QAction, 'actionTake_a_timed_picture')
self.Camera_Properties = self.findChild(QAction, 'actionProperties')
# Initializing Video
self.Start_Video.triggered.connect(self.Start_Video_Clicked)
self.Stop_Video.triggered.connect(self.Stop_Video_Clicked)
self.Camera_Properties.triggered.connect(self.Camera_Properties_Clicked)
def Video_Feed_Update(self, Image):
self.Video_Feed.setPixmap(QPixmap.fromImage(Image))
def Start_Video_Clicked(self):
self.Video_Feed_is_Running = True
self.thread = QThread()
self.Video_Thread = Worker()
self.Video_Thread.moveToThread(self.thread)
self.Video_Thread.ImageUpdate.connect(self.Video_Feed_Update)
self.thread.started.connect(self.Video_Thread.run)
self.thread.start()
def Stop_Video_Clicked(self):
self.Video_Thread.stop_video()
self.Video_Feed.setText("Your video starts here")
def Camera_Properties_Clicked(self):
self.CP = CameraParameters()
Initial_Exposure = self.Video_Thread.Camera_Initial_Parameters()
self.CP.Setup_Exposure(int(Initial_Exposure))
self.CP.Exposure_Calibration.connect(self.Video_Thread.Exposure_update)
self.CP.show()
class Worker(QObject):
ImageUpdate = pyqtSignal(QImage)
def run(self):
self.ThreadActive = True
self.Capture = cv2.VideoCapture(1, cv2.CAP_DSHOW)
self.Capture.set(3, 1920)
self.Capture.set(4, 1080)
while self.ThreadActive:
ret, frame = self.Capture.read()
if ret:
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Converting Video into QT5 readable format
qt_video_format = QImage(image.data, image.shape[1], image.shape[0], QImage.Format_RGB888)
qt_picture = qt_video_format.scaled(1280, 720, Qt.KeepAspectRatio)
self.ImageUpdate.emit(qt_picture)
def stop_video(self):
self.ThreadActive = False
self.Capture.release()
def Camera_Initial_Parameters(self):
return self.Capture.get(cv2.CAP_PROP_EXPOSURE)
def Exposure_update(self, value):
self.Capture.set(cv2.CAP_PROP_EXPOSURE, value)
class CameraParameters(QDialog):
Exposure_Calibration = pyqtSignal(int)
def __init__(self):
super().__init__()
uic.loadUi('Cam_Parameters.ui', self)
# Sliders
self.Exposure_Slider = self.findChild(QSlider, 'ExposureSlider')
self.Exposure_Slider.setRange(-10, 10)
self.White_Balance_Slider = self.findChild(QSlider, 'WBSlider')
self.White_Balance_Slider.setMinimum(-10)
self.White_Balance_Slider.setMaximum(10)
self.Brightness_Slider = self.findChild(QSlider, 'BrightnessSlider')
self.Brightness_Slider.setMinimum(0)
self.Brightness_Slider.setMaximum(300)
self.Saturation_Slider = self.findChild(QSlider, 'SaturationSlider')
self.Saturation_Slider.setMinimum(0)
self.Saturation_Slider.setMaximum(300)
self.Contrast_Slider = self.findChild(QSlider, 'ContrastSlider')
self.Contrast_Slider.setMinimum(-10)
self.Contrast_Slider.setMaximum(10)
self.Gamma_Slider = self.findChild(QSlider, 'GammaSlider')
self.Gamma_Slider.setMinimum(-10)
self.Gamma_Slider.setMaximum(10)
self.Sharpness_Slider = self.findChild(QSlider, 'SharpnessSlider')
self.Sharpness_Slider.setMinimum(0)
self.Sharpness_Slider.setMaximum(100)
# Sliders values
self.Exposure_Value = self.findChild(QLabel, 'Exposure_Value')
self.White_Balance_Value = self.findChild(QLabel, 'WB_value')
self.Brightness_Value = self.findChild(QLabel, 'Brightness_value')
self.Saturation_Value = self.findChild(QLabel, 'Saturation_value')
self.Contrast_Value = self.findChild(QLabel, 'Contrast_value')
self.Gamma_Value = self.findChild(QLabel, 'Gamma_value')
self.Sharpness_Value = self.findChild(QLabel, 'Sharpness_value')
# Connections
self.Exposure_Slider.valueChanged.connect(self.Exposure_sliding)
def Setup_Exposure(self, value):
self.Exposure_Slider.setValue(value)
self.Exposure_Value.setText(str(value))
def Exposure_sliding(self, value):
self.Exposure_Value.setText(str(value))
self.Exposure_Calibration.emit(value)
if __name__ == "__main__":
App = QApplication(sys.argv)
Root = MainFrame()
Root.show()
sys.exit(App.exec())
Cam_Parameters.ui, MainFrame.ui for the GUI
I admit I don't know why this worked, but changing the connect to a lambda function did the trick.
self.CP.Exposure_Calibration.connect(lambda x: self.Video_Thread.Exposure_update(x))

how to create video player in pyqt5 for windows to play mp4 using python code?

I tried to create video player for mp4 in windows 10 os. it is always showing error. i tried multiple method. but always failing
I tried code from this location https://pythonprogramminglanguage.com/pyqt5-video-widget/
QMediaPlayer error:
I tried python-vlc. it is working only when I already installed vlc player. if i uninstalled vlc player it is not working
vlc error :
File "<module1>", line 7, in <module>
File "D:\Portable_Python\App\Python\lib\site-packages\vlc.py", line 210, in <module>
dll, plugin_path = find_lib()
File "D:\Portable_Python\App\Python\lib\site-packages\vlc.py", line 173, in find_lib
dll = ctypes.CDLL(p)
File "D:\Portable_Python\App\Python\lib\ctypes\__init__.py", line 364, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 is not a valid Win32 application
>>>
tkvideoplayer
I tried code from this location https://github.com/PaulleDemon/tkVideoPlayer/blob/master/examples/sample_player.py
it is working fine. but I couldn't able it embed to pyqt5 window.
I attached example video file in this location
https://drive.google.com/file/d/1AaTzf3LBUMCztO8hYpF9muPUImVDX1s_/view?usp=sharing
please give me a best solution in any one module.
I added simple code below . this is playing .avi file and not playing .mp4 files
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QPushButton
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
import sys
class VideoPlayer:
def __init__(self):
self.video = QVideoWidget()
self.video.resize(300, 300)
self.video.move(0, 0)
self.player = QMediaPlayer()
self.player.setVideoOutput(self.video)
self.player.setMedia(QMediaContent(QUrl.fromLocalFile("D:/2.mp4")))
def callback(self):
self.player.setPosition(0) # to start at the beginning of the video every time
self.video.show()
self.player.play()
if __name__ == '__main__':
app = QApplication(sys.argv)
v = VideoPlayer()
b = QPushButton('start')
b.clicked.connect(v.callback)
b.show()
sys.exit(app.exec_())
As couldn't able to find direct player. I used cv module and ffpyplayer to integrate with pyqt5. it is temporarily resolved my issue. the code is given below.
from ffpyplayer.player import MediaPlayer
from PyQt5 import QtGui,QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import threading
import time
import cv2
from PIL import Image
import numpy
import os
class VideoPlayer:
def __init__(self, filename,volume = 1.0):
if not os.path.exists(filename):raise FileNotFound(filename)
self.close = False
self.state = None
self.frame = None
self.l=None
self.filename = filename
self.skip_interval=5
self.player = MediaPlayer(filename, ff_opts={'sync': 'audio', 'paused': False, 'volume': volume, 't': 1e7+1, 'ss': 0})
time.sleep(1)
self.duration=self.player.get_metadata()['duration']
handler_thread = threading.Thread(target=self.play, args=(), daemon=True)
handler_thread.start()
def play(self):
while True:
frame, self.val = self.player.get_frame()
if self.val == 'eof':self.close=True
if self.close == True:
self.player.toggle_pause()
self.player.close_player()
time.sleep(2)
break
if isinstance(self.val, str) or self.val == 0.0:waitkey = 32
else:waitkey = int(self.val * 100)
pressed_key = cv2.waitKey(waitkey) & 0xFF
if frame is None:continue
image, pts = frame
self.frame = (image, self.val)
x, y = image.get_size()
data = image.to_bytearray()[0]
image = Image.frombytes("RGB", (x, y), bytes(data))
image = cv2.cvtColor(numpy.array(image), cv2.COLOR_RGB2BGR)
self.frame=frame
if self.l!=None:
h, w, ch = image.shape
Image2 = QImage(image.data, w, h, ch * w, QImage.Format_RGB888)
self.pixmap=QPixmap.fromImage(Image2)
self.l.setPixmap(self.pixmap)
self.l.setFixedWidth(self.l.pixmap().width())
self.l.setFixedHeight(self.l.pixmap().height())
self.l.parent().update()
del image
def seek_p(self):
if int(self.player.get_pts()) + self.skip_interval < int(self.duration):
self.player.seek(self.skip_interval, relative=True, accurate=False)
def seek_m(self):
if int(self.player.get_pts()) - self.skip_interval > 0:
self.player.seek(-self.skip_interval, relative=True, accurate=False)
class ed(QWidget):
def __init__(self,parent=None):
super().__init__()
vl=QVBoxLayout()
self.setLayout(vl)
pb=QPushButton('play');vl.addWidget(pb);pb.clicked.connect(self.play)
pb=QPushButton('stop');vl.addWidget(pb);pb.clicked.connect(self.close)
pb=QPushButton('pause');vl.addWidget(pb);pb.clicked.connect(self.pause)
pb=QPushButton('seek+');vl.addWidget(pb);pb.clicked.connect(self.seek_p)
pb=QPushButton('seek-');vl.addWidget(pb);pb.clicked.connect(self.seek_m)
pb=QPushButton('mute');vl.addWidget(pb);pb.clicked.connect(self.mute)
pb=QPushButton('vol+');vl.addWidget(pb);pb.clicked.connect(self.vol_p)
pb=QPushButton('vol-');vl.addWidget(pb);pb.clicked.connect(self.vol_m)
self.l=QLabel();vl.addWidget(self.l)
def play2(self):
self.player=VideoPlayer('D:/2.mp4')
self.player.l=self.l
def play(self):
self.t1=threading.Thread(target=self.play2)
self.t1.start()
def pause(self):self.player.player.toggle_pause()
def seek_p(self):self.player.seek_p()
def seek_m(self):self.player.seek_m()
def mute(self):
try:
if (self.player.player.get_volume()>0.0):self.player.player.set_volume(0.0)
else:self.player.player.set_volume(1.0)
except:pass
def vol_p(self):
self.player.player.set_volume(self.player.player.get_volume() + 0.1)
print(self.player.player.get_volume())
def vol_m(self):
self.player.player.set_volume(self.player.player.get_volume() - 0.1)
print(self.player.player.get_volume())
def close(self):
try:
self.player.close=True
time.sleep(1)
except:pass
def closeEvent(self, event):
try:
self.player.close=True
time.sleep(1)
except:pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window=ed()
window.showMaximized()
sys.exit(app.exec_())

Python 4-channel video streaming program QThread speedup problem

It is a 4 channel video streaming program.
When running with QThread, if you stream 4 at the same time, the speed of the video will slow down and CPU usage will be 100%.
Can I change this to multiprocessing?
I want to speed up a 4 channel cctv program.
threadMode.py
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QImage
import cv2
class StreamingThread(QThread):
changePixmap = pyqtSignal(QImage)
def __init__(self):
super(StreamingThread, self).__init__()
self.running = True
self.camUrl = None
self.Qsize = None
self.cap = None
def setRtsp(self, camUrl):
self.camUrl = camUrl
def setSize(self, Qsize):
self.Qsize = Qsize
def run(self):
try:
self.cap = cv2.VideoCapture(self.camUrl)
if self.cap.isOpened():
while self.running:
success, frame = self.cap.read()
if success:
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgbImage.shape
bytesPerLine = ch * w
convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertToQtFormat.scaled(self.Qsize, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
else:
print("RTSP(RTMP) Video Streaming Fail")
self.stop()
except Exception as e:
print(e)
self.stop()
def stop(self):
if self.running:
self.running = False
print("Streaming Stop")
self.quit()
Main.py
def setChannel1(self):
# Index 0: No Channel
if self.chComboBox1.currentIndex() == 0:
self.mStreamingThread1.terminate()
sched = BackgroundScheduler()
sched.add_job(self.clearChannel, 'date', run_date=datetime.datetime.now() + datetime.timedelta(seconds=1), args=[self.cctvStreaming1])
sched.start()
else:
ip, url, channel, boxId = self.findUrl(self.chComboBox1)
if url != '' and channel != '':
self.mStreamingThread1.terminate()
self.mStreamingThread1.wait(1)
self.mStreamingThread1.setRtsp(url)
self.mStreamingThread1.setSize(self.cctvStreaming1.size())
self.mStreamingThread1.changePixmap.connect(self.setImage1)
self.mStreamingThread1.start()
self.show()
logger.info("Channel1 Streaming Success")
#pyqtSlot(QImage)
def setImage1(self, image):
self.cctvStreaming1.setPixmap(QPixmap.fromImage(image))

python: tkinter to display video from webcam and do a QR scan

I have been trying to create a tkinter top level window that streams video form webcam and do a QR scan. I got this QR scan code from SO and another code that just updates images from webcam instead of streaming the video on a tkinter label.
and i tried to combine these both so that a toplevel window with a label updating image from webcam and a close button to close the toplevel window. And while it streams the images, it can scan for QR code and if a scan is successful, the webcam and the toplevel window gets closed.
here is what i tried.
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()
Problem is,
I actually wanted to display the video on the Toplevel window, not the OpenCV window
at the same time do a QR Scan,if a read is sucessfull, the Toplevel window should close without abruptly closing webcam(because, when i try to use self.cam.release() or cv2.destroyAllWindows() my webcams lights or on even if i forcefully terminate the programs compilation).
Now what i get is a separate window created by OpenCV that streams video inside. But i don’t want that window, instead i want the video to be displayed on the tkinter's toplevel window. also when there is a sucessfull read, the webcam stucks at the final image it reads.
i tried to remove the line that was responsible for OpenCV window, inside the run method of BarcodeScanner class
cv2.imshow(self.WINDOW_NAME, img[1])
it still showed up with a different window with no output, and if i try to close that window, it created another one similar and recursively.
UPDATE:
As i noticed i made some silly mistakes without understanding of some lines in cv2, i made some change on the code by adding the toplevel window code into the run method of the class(im not sure if this is a right way).
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()
now, I can get the image on the Toplevel window, But i dont know how to close the webcam.
condition 1: when i show a QR code to scan, it reads it successfully and webcam quits without any error.
condition 2: when i click the close button on the toplevel window(say if user doesn't want to do any scan and just want to close the webcam) i get error saying
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)
I am writing this application for Linux, Mac and Windows machine. How can i close or terminate the webcam safely.
Your program has two threads, the main thread and the worker thread that reads frames from the camera. When the close button is clicked, it happens in the main thread. After self.cam.release() the object self.cam is probably in an unusable state, and when a method of self.cam is called by the worker thread, there may be some trouble. Maybe the implementation of cv2.VideoCapture is faulty and it should throw some exception when that happens.
Accessing tkinter widgets from other thread than the main thread may also cause problems.
For clean program termination, creating an instance of threading.Event and then checking for event.is_set() at some point in the work thread could work. For example
def destroy_video_window():
self.stop_event.set()
video_window.destroy()
and then in the worker thread
while True:
if self.stop_event.is_set():
break
for i in range(0, self.CV_SYSTEM_CACHE_CNT):
self.cam.read()
There are several things that could be done in other way, the following is a modified version of the code. It avoids calling tkinter methods from other thread than the main thread, event_generate() being the only tkinter method called by the worker thread. Explicit polling is avoided by emitting virtual events, for example <<ScannerQuit>>, that are placed in the tkinter event queue.
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()

Categories

Resources