1

I am trying to start the picamera process on the main thread and pass it as an argument to a new process, where I would take photos with the camera while the main process runs. However, the camera does not produce any images when operated in the other process, and the code hangs at p1.join(). Here is the code.

import time
from multiprocessing import Process
import picamera

def snapPhoto(camera):
    for i in range(3):
        camera.capture('threaded_photo_' + str(i) + '.jpg')
        # Tried to use video port with no luck
        #camera.capture('threaded_photo_' + str(i) + '.jpg', use_video_port=True) 

def runCameraPreview():

    #for i in range(3):
    with picamera.PiCamera() as camera:
        # Get the camera ready
        time.sleep(2)
        print("Starting process")
        # try image capture in a thread
        p1 = Process(target=snapPhoto, args=(camera,))      
        p1. start()
        # Do any processing
        print("Waiting")
        p1.join() # Wait for the process to return

runCameraPreview()

I simply create the camera object and pass it as an argument (one object in tuple) to the Process. The code then waits for the images to be taken. However, it never takes the images. How can I make the camera to take photos in a separate thread?

Edit: I will need the camera object in the main thread so initializing the camera on the side process in not an option.

PS. You have to terminate the code by force, it will not exit. Moreover, a process using the camera resource will be open in the background until killall python is executed.

2

Someone wrote to me with a very similar query a few months ago. The basic problem is this: only one process can have a PiCamera instance at any given time. The camera's firmware is built on the assumption that a single process will be using it; break that assumption and things will go horribly wrong.

In your case you're using the multiprocessing module which instantiates new processes not new threads (if you were just using threads, this would probably work assuming you didn't try and control the camera from more than one thread at a time).

Let's walk through your script and I'll describe what's going on "behind the scenes":

  1. You run your script which starts a new process (let's call it process 1).
  2. The runCameraPreview function is called.
  3. At the top of runCameraPreview you create a PiCamera instance. The camera is now initialized and tied to this process.
  4. In the middle of runCameraPreview you set up a new process to run the snapPhoto function (p1 = Process...).
  5. Next the new process is started (p1.start()). This forks the current process to create a new one (process 2).
  6. The new process runs the snapPhoto function. It tries to access the camera using its inherited objects, but the camera is tied to process 1. Process 2 almost certainly hangs at this point.
  7. Back in process 1, we're waiting on process 2 to finish (pi1.join()) but it never will because process 2 has hung trying to access a duplicated camera object.

So, you need to keep your camera access within a single process. To that end you could stick with a single process, but if you're determined to use multiprocessing you could simply move the camera initialization entirely into the background process:

import time
from multiprocessing import Process
import picamera

def snapPhoto(camera):
    with picamera.PiCamera() as camera:
        # Get the camera ready
        time.sleep(2)
        for i in range(3):
            camera.capture('threaded_photo_' + str(i) + '.jpg')
            # Tried to use video port with no luck
            #camera.capture('threaded_photo_' + str(i) + '.jpg', use_video_port=True) 

def runCameraPreview():

    #for i in range(3):
        print("Starting process")
        # try image capture in a separate process
        p1 = Process(target=snapPhoto)      
        p1. start()
        # Do any processing
        print("Waiting")
        p1.join() # Wait for the process to return

runCameraPreview()

However, you can't run more than a single process (simultaneously) that calls snapPhoto in this case, so it's a bit pointless to split off into a separate process. Generally, the cases involving picamera and multiprocessing use one process to control the camera and capture images, and then multiple other processes to perform computations on those images. Hence, the camera object doesn't wind up getting passed around, only the image data (which is absolutely fine).

  • Thanks, I understand the problem now! I will try with threads instead of processes to see if it can be done and if not, then resort to single process. I was trying to snap photos without freezing video capture here, so that is why I decided to go with multiprocessing. – milez Aug 15 '16 at 4:55
  • Ah, there is a recipe for that using the splitter to record on one port and capture on another, but to avoid dropping frames you have to do a video port capture (because still captures involve a mode switch - the details are in the camera hardware chapter) – Dave Jones Aug 15 '16 at 6:16
  • Yes, I actually found that later and it works pretty well, still causes a slight fps drop but I have to tweak the settings a bit I guess – milez Aug 15 '16 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.