I'm trying to understand why some functions fail with threading in python.
For example, I'm trying to use VideoCapture to grab an image from a webcam.
This example works fine:
from VideoCapture import Device
cam = Device()
cam.saveSnapshot('image.jpg')
But when I put it on a thread I get an error.
import threading
from VideoCapture import Device
def grab():
cam = Device()
cam.saveSnapshot('image.jpg')
thr = threading.Thread(target=grab)
thr.start()
thr.join()
File "C:\Program
Files\Python36\lib\site-packages\VideoCapture__init__.py", line 60,
in init
self.dev = vidcap.new_Dev(devnum, showVideoWindow) vidcap.Error: Creation of the filter graph failed.
According to this reference, this function is not thread-safe. So is there any workaround to bypass similar issues like this? I tried to use threading.lock but got the same error. If I need to change the code, which part should I check?
I couldn't find a direct way to fix it, but importing the module inside the thread fixed that, I'm not sure if this applies to other modules.
import threading
def grab():
from VideoCapture import Device
cam = Device()
cam.saveSnapshot('image.jpg')
del cam
thr = threading.Thread(target=grab)
thr.start()
thr.join()
Related
I have written a Python script that runs on a Raspberry Pi and utilizes the PiCamera library to capture video, the Python Image Library (PIL) to extract individual frames, and then does some image processing on it using DIPLib and OpenCV. The goal of the program is to continuously capture frames of 3D printer filament and return the diameter value. The actual image processing portion works just fine- it's the frame capture that is causing me issues.
I am following the PiCamera Rapid Capture and processing tutorial from PiCamera and using the Python Threading library as they have done to ideally utilize more of th Pi's processor for processing and not getting bottlenecked and falling behind.
The implementation of this code is built to "Drop Frames" when there are not any threads available for processing. As I understand it, this should prevent the Pi from storing any extra frames in the buffer for processing, thus preventing a memory overflow (not sure if that's the correct terminology) from happening. Unfortunately this is exactly what is happening.
I am running the PiCamera at about 3 frames-per-second which gives ~10 threads the ability to process all the incoming images, that is until the memory starts to overflow. However, if I leave the script running for 5-10 minutes, the memory (as shown using htop) slowly compounds until it reaches maximum capacity- at which point the script basically drops all incoming frames.
UPDATE: here is the error it shows:
Exception has occurred: MemoryError
exception: no description
File "/home/pi/Desktop/FilamentPuller_01/pi_camera_threading_03.py", line 45, in run
img = np.array(Image.open(self.stream)
My theory is that the video recording functionality of PiCamera is holding a buffer of some sort, but I am not sure how to see it or how to stop it from doing that. I've been using VSCode on the Pi to debug, and each thread doesn't seem to holding any more data at a time than they should- essentially there should be no reason for them to compound more data from one cycle to the next as all the variables are reused.
I have included my code below, please let me know what other information I can provide to help with solving this issue. Thank you for any insight you might have
import io
import sys
import time
import threading
import cv2
import numpy as np
import os
import picamera
from PIL import Image
import csv
from diameter_detection import diameter_detection
from moving_average import MovingAverageFilter
from serial_write import serial_write
##### CAMERA SETTINGS ######
focalValue = 40 # focus
cameraResolution = (1080, 1000)
cameraZoom = (0.3,0,0.3,0.8)
cameraFrameRate = 1
# create an array for storing filtered diameter values
filtered_dia_meas = []
# create moving average filter
ma5_filter = MovingAverageFilter(2)
class ImageProcessor(threading.Thread):
def __init__(self, owner):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.owner = owner
self.start()
def run(self):
# This method runs in a separate thread
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# Read the image and do some processing on it
img = np.array(Image.open(self.stream))
try:
diameter = diameter_detection(img)
except:
serial_write(0)
print('Could not read diameter, pausing and retrying...')
time.sleep(0.1)
# add the diameter to the filter
ma5_filter.step(diameter)
#filtered_dia_meas.append(ma5_filter.current_state())
# display the current filtered diameter to the Terminal
print(ma5_filter.current_state())
try:
# attempt to send the diameter to the connected serial device
serial_write(ma5_filter.current_state())
except:
print('Serial write failed!')
# Set done to True if you want the script to terminate
# at some point
#self.owner.done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return ourselves to the available pool
with self.owner.lock:
self.owner.pool.append(self)
class ProcessOutput(object):
def __init__(self):
self.done = False
# Construct a pool of 10 image processors along with a lock
# to control access between threads
self.lock = threading.Lock()
self.pool = [ImageProcessor(self) for i in range(10)]
print('Threaded processes created')
self.processor = None
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame; set the current processor going and grab
# a spare one
if self.processor:
self.processor.event.set()
with self.lock:
if self.pool:
self.processor = self.pool.pop()
else:
# No processor's available, we'll have to skip this frame
print('Frame Skipped!')
self.processor = None
if self.processor:
self.processor.stream.write(buf)
def flush(self):
# When told to flush (end of recording), shut
# down in an orderly fashion. First, add the current processor
# back to the pool
if self.processor:
with self.lock:
self.pool.append(self.processor)
self.processor = None
# Now, empty the pool, joining each thread as we go
while True:
with self.lock:
try:
proc = self.pool.pop()
except IndexError:
pass # pool is empty
proc.terminated = True
proc.join()
with picamera.PiCamera(resolution=cameraResolution) as camera:
print('Succesfully created camera object')
camera.framerate = cameraFrameRate
camera.zoom = (0.3,0,0.3,0.8)
# set focus motor
os.system("i2cset -y 0 0x0c %d %d" % (focalValue,0))
print('Camera focus set')
time.sleep(2)
print('Starting recording...')
output = ProcessOutput()
camera.start_recording(output, format='mjpeg')
while not output.done:
camera.wait_recording(1)
camera.stop_recording()
I'm programing a GUI with tkinter that uses cv2 to extract the video stream from the computer camera. To do that, a Thread reloads the image using a while loop.
I have been using Locks so both Threads (the main one and the auxiliar) end their process as expected. However, i'm not able to do that because of a DeadLock that I can't identify.
The code is the following:
import tkinter as tk
import cv2
import time
import sys
import threading
from PIL import ImageTk, Image
class Application:
gui_root = None
gui_panel = None
frame = None
video_capture = None
stop_event = None
thread = None
thread_lock = None
def __init__(self):
self.gui_root = tk.Tk()
self.gui_root.geometry("640x480")
self.gui_root.resizable(False, False)
self.video_capture = cv2.VideoCapture(0)
self.stop_event = threading.Event()
self.thread_lock = threading.Lock()
self.thread = threading.Thread(target=self.load_frame, args=())
self.thread.start()
self.gui_root.wm_protocol("WM_DELETE_WINDOW", self.on_close)
def start(self):
self.gui_root.mainloop()
def load_frame(self):
print('Starting load_frame')
while self.thread_lock.acquire() and not self.stop_event.is_set():
_, self.frame = self.video_capture.read()
self.frame = cv2.resize(self.frame, (640, 480))
c_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
c_image_tk = ImageTk.PhotoImage(Image.fromarray(c_image))
if self.gui_panel is None:
self.gui_panel = tk.Label(image=c_image_tk)
self.gui_panel.place(width=640, height=480, x=0, y=0)
else:
self.gui_panel.configure(image=c_image_tk)
self.thread_lock.release()
print("End of while")
self.thread_lock.release()
print('End of Process 2')
def on_close(self):
self.stop_event.set()
print("Event set")
with self.thread_lock:
self.video_capture.release()
self.gui_root.destroy()
app = Application()
app.start()
The most likely problem here is that you're attempting to do Tkinter stuff, like creating a new tk.Label, from a background thread, and that's not allowed.
My first guess is that what's happening is that the Tk code on the background thread is just hanging—which isn't really a deadlock, but since you've got a thread hanging forever while holding a lock, nobody else will ever acquire that lock, so it's much the same effect.
The only way to solve this is to have your background thread(s) post messages to a queue requesting GUI stuff to get done for them, and have the main thread poll that queue each time through the event loop (e.g., via an after function).
The old mttkinter project wrapped this up transparently, but unfortunately it was never updated for modern Python. A few years ago, I slapped together a quick port (at https://github.com/abarnert/mttkinter), but I never tested it rigorously, and I don't know if anyone's ever used it for anything serious.
But you could try this at least for a quick test. If it makes no difference, then you've probably ruled out Tk threading issues; if it makes a difference, great (although that difference could be "now it doesn't freeze up, but it crashes 50% of the time—in which case you're not done, and can't trust mttkinter as-is, but at least you know that Tk threading is the thing you have to solve).
from picamera import PiCamera
from time import sleep
camera = PiCamera
camera.hflip = True #camera ad effetto specchio (flip orizzontale)
def registra_video():
camera.start_recording('video.h264')
camera.start_preview()
def stop_video():
camera.stop_recording()
camera.stop_preview()
while True:
registra_video()
This is a first sketch, then the function will be maneged by the output of a motion sensor, but now i've put the while in loop to try to start recording and the preview, but i get the error explained in the title. Why?
P.s sorry i'm noob in python and raspberry development.
I have a problem with pyudev library usage.
I want to have a program which detects USB plug in and prints something to console. Here's the code i have:
import glib
import os
import sys
from pyudev import Context, Monitor
from pyudev.glib import GUDevMonitorObserver as MonitorObserver
def device_event(observer, device):
print 'yep'
context = Context()
monitor = Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')
observer = MonitorObserver(monitor)
observer.connect('device-added', device_event)
monitor.start()
glib.MainLoop().run()
The problem is when i run the script it seems that device_event function gets called twice.
The output i get when i plug in an USB device is:
yep
yep
I searched all over but could not find an answer
Please help
Thank you
You get an event for the device enumeration and then separate events for each enumerated interface.
Okay, time for another question/post...
So currently i am trying to develop a simple python program that has a webkit/ webpage view and a serial port interface.. Not that it should matter, but this is also running on a raspberry pi.
The following code works fine.. But it will freeze the system as soon as i uncomment the serial port line that you can see commented out.
The day has been long and this one for some reason has my brain fried.. Python is not my strongest point, but mind you this is just a quick test script for now... Yes i have used google and other resources...
#!/usr/bin/env python
import sys
import serial
import threading
import time
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
sURL = ""
sURL2 = ""
objSerial = serial.Serial(0)
def SerialLooper():
global objSerial
if objSerial.isOpen() == True:
print("is_responding")
#objSerial.write("is_responding")
time.sleep(10)
SerialLooper()
class TestCLASS(object):
def __init__(self):
global sURL
global sURL2
global objSerial
objSerial = serial.Serial(0)
sURL = "http://localhost/tester"
app = QApplication(sys.argv)
webMain = QWebView()
webMain.loadFinished.connect(self.load_finished)
webMain.load(QUrl(sURL))
webMain.show()
thread = threading.Thread(target=SerialLooper)
thread.start()
sys.exit(app.exec_())
def load_finished(boolNoErrors):
global sURL
print("Url - " + sURL)
#something here
#something else here
newObjClass = TestCLASS()
EDIT
Futher on this, it appears its not the multithreading but the serial.write()
It has been a while since I used serial, but IIRC it is not threadsafe (on Windows at least). You are opening the port in the main thread and performing a write in another thread. It's a bad practice anyway. You might also consider writing a simple single-threaded program to see if the serial port is actually working.
PS Your program structure could use some work. You only need one of the global statements (global objSerial), the rest do nothing. It would be better to get rid of that one, too.
And the recursive call to SerialLooper() will eventually fail when the recursion depth is exceeded; why not just use a while loop...
def SerialLooper():
while objSerial().isOpen(): # Drop the == True
# print something
# write to the port
# Sleep or do whatever