0

I have four functions; three camera functions to (take a picture save the image, save the path to a CSV file), the fourth function is to grab data from a serial connection via an arduino. Each function works as intended independently and via normal multiprocessing (without join()). I cannot use the join() method because of the way the opencv function work. I understand that the join method works, where it waits for the child processes to complete before running it again.

I need to be able to return a value (Boolean: True/ False or 0/1) from the arduino function to the camera function before starting again. Arduino code takes the longest and needs time to run. generic code below

import cv2
import multiprocessing
import Serial


def camera1():
    global cap1
    cap1 = cv2.VideoCapture(0)
    while True:
        _, frame1 = cap1.read()
        cv2.imshow('frame1', frame1)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap1.release()


def camera2():
    global cap2
    cap2 = cv2.VideoCapture(1)
    while True:
        _, frame2 = cap2.read()
        cv2.imshow('frame2', frame2)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap2.release()



def camera3():
    global cap3
    cap3 = cv2.VideoCapture(2)
    while True:
        _, frame3 = cap3.read()
        cv2.imshow('frame3', frame3)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap3.release()


def ard_serial():
   """
   Serial Stuff happens here
   When Complete sends value to cam functions
   to start again.
   """

if __name__ == '__main__':
   for _ in range(20)
       p1 = multiprocessing.Process(target=camera1)
       p1.start()
       p2 = multiprocessing.Process(target=camera2)
       p2.start()
       p3 = multiprocessing.Process(target=camera3)
       p3.start()
       p4 = multiprocessing.Process(target=ard_serial)
       p4.start()
       """
       p1.join()
       p2.join()
       p3.join()
       p4.join()
       """

I need to have all four functions to start at the same time and have the cam functions wait for the arduino function to finish before starting again. What can I use for this? I wasn't sure if I have to use Queue or something different. Plus most examples have just one worker function that returns something. I need one function to send a return value to another function. join() method won't work because of the potentially never ending while loop, until it is complete

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • https://stackoverflow.com/questions/10721915/shared-memory-objects-in-multiprocessing – Joe Aug 09 '19 at 05:51
  • https://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing – Joe Aug 09 '19 at 05:51
  • Hey @Joe, Thanks for the reply. I still don't understand how adding 'Value()' and 'Lock()' to my code. Yes, they all see it now but I can't update the 'Value()' method from my Arduino function. For example, if I added val.value = int(input("0 or 1")), the cam functions won't wait for arduino function to finish. This is in response to the link you sent [link](https://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing), it's getting me closer but not fully what I need. – JeffTheMess Aug 09 '19 at 11:51
  • One of the links shows how to pass value to a process. or https://docs.python.org/3/library/multiprocessing.html#sharing-state-between-processes – Joe Aug 09 '19 at 14:17
  • why do you think you need `multiprocessing` here? I think I'd just run it all from a loop in a single main process – Sam Mason Aug 13 '19 at 16:19
  • Sam, I need to capture images and serial data in parallel; to start at the same and finish when the serial data I need is done...what would you recommend? – JeffTheMess Aug 13 '19 at 20:04
  • your comments suggest that you only want to capture video frames after a message from the serial port, is that correct? or do you actually want to capture frames the whole time and only stop when you get a serial message – Sam Mason Aug 14 '19 at 08:58
  • I need to capture 4 actions in parallel. 3 camera frame and 1 serial data. Since the camera will capture an image faster then the data from the serial. I need to "pause" the camera functions and wait until the serial data to capture what i need from it, then start the process a couple thousand time. They must remain in sync. I needed a method to force the camera functions to wait until the serial data is complete. I hope i made myself a more clear, it hard to explain parallel process. – JeffTheMess Aug 14 '19 at 23:47

2 Answers2

0

The easiest way is to use events for signaling between the different processes.

Events are a very basic way for sync processing. They can be set (=True) or cleared (=False). They also have a wait-function that blocks until the event is set to True. As they are thread-safe, they can be shared between processes.

Documentation Event

Something like:

import cv2
import multiprocessing
import Serial


def camera1(e):
    global cap1
    cap1 = cv2.VideoCapture(0)
    while True:
        e.wait()
        _, frame1 = cap1.read()
        cv2.imshow('frame1', frame1)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap1.release()


def camera2(e):
    global cap2
    cap2 = cv2.VideoCapture(1)
    while True:
        e.wait()
        _, frame2 = cap2.read()
        cv2.imshow('frame2', frame2)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap2.release()



def camera3(e):
    global cap3
    cap3 = cv2.VideoCapture(2)
    while True:
        e.wait()
        _, frame3 = cap3.read()
        cv2.imshow('frame3', frame3)

        k = cv2.waitKey(5)
        if k == 27:
            break
       """
       Saves image and add to csv file
       while loop checking for ard_serial is complete to repeat
       """
    cap3.release()


def ard_serial(e):
   """
   Serial Stuff happens here
   When Complete sends value to cam functions
   to start again.
   """
        e.clear()
        #do your stuff
        e.set()

if __name__ == '__main__':
   e = multiprocessing.event()
   for _ in range(20)
       p1 = multiprocessing.Process(target=camera1, args=[e,])
       p1.start()
       p2 = multiprocessing.Process(target=camera2, args=[e,])
       p2.start()
       p3 = multiprocessing.Process(target=camera3, args=[e,])
       p3.start()
       p4 = multiprocessing.Process(target=ard_serial, args=[e,])
       p4.start()
       """
       p1.join()
       p2.join()
       p3.join()
       p4.join()
       """
RaJa
  • 1,471
  • 13
  • 17
  • Hey thanks for the reply! I feel like this would be the best solution but it not working correctly. Just to test it with my cameras, I ran the code exactl as you stated; I just added a simple for loop with a sleep of 500mS . I add the loop in between the e.clear() and the e.set()....assuming I have it right: 4 processes should start, the cameras take their pic and wats for the flag to change with e.wait(), mean while the ard_serial set the event flag to false runs throught the loop sent the event flag to true and all four processes start again... – JeffTheMess Aug 24 '19 at 04:29
0

I'm not sure if you need multiprocessing at all. the following code might help highlight/solve some of the issues you're having:

import cv2

caps = {}
for i in range(3):
    cap = cv2.VideoCapture(i)
    # make sure we don't get too far behind
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
    caps[i] = cap

I start by opening all the capture devices and setting their buffer size to 1 frame. otherwise if you're waiting for serial activity before frames you'll tend to get older/buffered frames rather than a more recent one that you actually want.

next we run our main loop

frame = 0
while True:
    frame += 1

    # TODO: wait/check/handle for Serial activity

    # clear out any old/buffered frames
    for cap in caps.values():
        cap.read()

    # read recent frames
    for i, cap in caps.items():
        _, img = cap.read()
        # save to a file and display in window
        cv2.imwrite(f'output/{frame:06}-{i}.jpeg', img)
        cv2.imshow(f'cap {i}', img)

    # handle GUI events/check for keys
    if cv2.waitKey(1) == 27:
        break

you didn't include any actual code that you want for checking serial activity, but you should be able to use any blocking functions there. when serial code finishes, it reads and discards buffered frames from all cameras, then saves the subsequent frames out to disk. note that all the clearing should all happen at the same time so the (asynchronous) camera devices can start streaming their next frame together, and then we go over all devices again to pick up frames when they're ready

given what you've written above, I think this should be a reasonable template for solving your problem. it would of course be possible to use multiprocessing, but I think it just complicates what you're doing here for no benefit

Sam Mason
  • 15,216
  • 1
  • 41
  • 60