Faster video processing
Forums:
My Python skills are definitely lacking!
My DiddyBorg is fitted with a USB web camera mounted on a servo which enables it to look around, use openCV (cv2) to find a target across the room (a bulb in a jam jar wrapped with blue film at the moment) and direct Diddy towards it. It needs to do this several times as Diddy is also avoiding obstacles on the route using ultrasound sensors and needs to find the target after each avoiding maneuver. There is also a static CSI camera which is used, again using cv2, to identify animal shapes on cards around the course.
Although I have got this all working it is a very slow process. Because PiCamera will not work with the web camera I am using cv2 to capture the images for processing, as in cap = cv2.VideoCapture(0) ..... success, frame = cap.read().
I have been studying your code in diddyJoyBall which uses several threads running at the same time to build a pool of images ready for processing, which I am guessing speeds up the processsing. I believe it is based on the code for rapid capture and processing in the PiCamera documentation? Most of the code is about the threading aspects and I have been trying to write code which does the same thing using the images I get from cv2 rather than the images from "camera.capture_sequence(.....)" used in diddyJoyBall but, to be blunt, I am finding it very difficult.
I wondered if you have any ideas on how I can achieve this? I would be most grateful for a few pointers.
piborg
Wed, 02/15/2017 - 14:08
Permalink
Multi-threaded image processing
If you are using either a Raspberry Pi B+, 2, or 3 then threading is definitely the way to go.
Without threading the script can only really make use of one of the four cores.
This means in an ideal situation the processing could run up to four times as fast :)
We are also using
cv2.VideoCapture(0)
now with Formula Pi.The Pi camera support has improved a lot since we wrote the DiddyBorg scripts and we have found it is faster than the older PiCamera based code.
This also has the advantage of the same code works with both the Pi Camera and a USB web camera :)
The OpenCV support for the Pi Camera can be setup by running this command:
The threading is a bit more complex as you have create the threads by hand.
This is not too difficult with Python, but understanding thread can be a bit tricky.
Below is the basic skeleton code we use for getting image processing code to run in multiple threads.
The actual processing code goes into the
ProcessImage
function inside theImageProcessor
class.The
ImageCapture
class is responsible for getting frames from the camera.It runs in its own thread, but uses very little processing time in practice.
What it does is wait until there is at least one processing thread waiting (held in
processorPool
).It takes the oldest thread out of this pool and then gives it the next frame returned from calling
cap.read
and sends it an event notification.This continues until the global
running
flag becomesFalse
.There should only be one of these threads running.
The
ImageProcessor
class gets passed the new frame and event notification each time we want it to process a frame from theImageCapture
thread.Each runs in its own thread and adds itself back into the list of waiting threads (
processorPool
) after theProcessImage
function has finished.We usually run four at the same time, but you should be able to have any number from one upwards :)
Our skeleton threading processing code:
il diavolo
Wed, 02/15/2017 - 21:54
Permalink
Fantastic!
The support you guys give to we users is amazing. That's exactly what I was looking for, thank you. I shall go away and cogitate!
il diavolo
Thu, 02/16/2017 - 20:26
Permalink
frameLock
Two questions if I may.
Looking through your skeleton code it looks as though "frameLock" is not defined. Should this be defined as a "threading.Lock()" object?
If so, and If I run 2 instances of "ImageCapture()", one for each camera, I assume I should use a separate "frameLock" for each which I could pass by reference, eg "def __init__(self, lock)", as a global would affect both instances. Similarly for "processorPool"?
Or have I completely got the wrong end of the stick as far as threading is concerned?
piborg
Fri, 02/17/2017 - 13:45
Permalink
Oops :)
Looks like I managed to loose a line somewhere, as you suspect it should be
at the bottom of the "shared values" section.
The
frameLock
object is used to protect theprocessorPool
list from being altered in two threads at the same time.Put simply it forces the threads to run one at a time when changing the
processorPool
list.If you have different processing threads for handling the processing of each camera then you probably will want to create a second
processorPool
and correspondingframeLock
object for the second camera.On the other hand if the processing is using the same threads for both the first and second camera then the
ImageCapture
objects can share theprocessorPool
andframeLock
:)il diavolo
Fri, 02/17/2017 - 21:19
Permalink
Thanks
Thanks.
il diavolo
Mon, 04/17/2017 - 22:38
Permalink
Number of threads
I have been experimenting with the image capture and image processing threads using u4l2 as you posted above. These work well and I can drive Diddy (although quite slowly) around and the web camera mounted on a servo will keep track of the target.
I initially ran the program with processingthreads = 4, as per your code. However, as far as I can tell there is absolutely no difference in performance between running only 1 thread, 4 threads or even 16 threads.
The offset between the centre of the image and the centre of the cv2 contours is passed to a global variable which is picked up by a function called from the main program (thus unthreaded) which then adjusts the servo by the required amount to keep the target as close to the centre as possible.
I have also tried including this centreTarget() function in the image processing thread but again I can detect no difference in performance. I wonder if the Ultraborg and servo are the bottleneck here. Any thoughts?
piborg
Tue, 04/18/2017 - 14:19
Permalink
Processing speed
I presume that by performance you are talking about how "fast" the code tracks the target.
My best guess is that the frame rate you have for the processing is low enough that a single thread can keep up with it. I would suggest increasing it and see what happens, I suspect the maximum for your web camera to be something like 30 or 60 frames per second.
What you should find is that as long as the code keeps up the higher frame rate will track things better. If the number is too high and the code does not keep up it will start to go out-of-sync and become delayed the longer the script is running.
clankerr02
Sun, 11/11/2018 - 21:52
Permalink
Frame rate Diddyborg red Web
In laymens terms does the above code increase the frame rate in the Web UI? if so where would it go within the Diddyborg red web ui script?
if not it there a way to increase the frame rate and reduce the lag?
cheers
John
piborg
Mon, 11/12/2018 - 17:19
Permalink
Increasing the frame rate
The frame rate was kept low so that things run properly even on a weak WiFi signal.
The settings are at the top of the script:
The
frameRate
is how fast the camera is reading images on the Pi and thedisplayRate
is how fast the WebUI gets new images. Start by increasingdisplayRate
and see if things improve. To go above 10 you will also need to increaseframeRate
.As for lag, that is entirely dependant on how quickly the camera reads images and how long the network takes to send it. You can increase the
frameRate
value to read from the camera faster, which might help. There is not much else you can really do it improve it.