Search code examples
pythonimage-processingparallel-processingbufferframe-rate

Is there any way to write a for loop in python which will grab images from camera and save it parallelly?


I'm using a basler camera to take images and also using pypylon to interface camera software(pylon) with python

I want to create two parts

  1. grab images (i.e 1000 or 2000)with some specific exposure time and shutter speed.

  2. and save it parallelly without disturbing fps.

    Or is there any way to save them in a buffer and use it afterward which won't affect fps?

Right now my concern is to save images without affecting fps Thanks

Following is my code

import pypylon
pypylon.pylon_version.version
available_cameras = pypylon.factory.find_devices()
available_cameras
cam = pypylon.factory.create_device(available_cameras[0])
cam.opened
cam.open()

cam.properties['AcquisitionFrameRateEnable'] = True
cam.properties['AcquisitionFrameRate'] = 1000
cam.properties['ExposureTime']
cam.properties['ExposureTime'] = 1000
#import matplotlib.pyplot as plt
from scipy.misc import imsave 
count=0
for image in cam.grab_images(30):
    count +=1
    a=str(count)
    b=str('I:/'+ a+'.png')
    imsave(b,image)

Solution

  • First things first: scipy's imsave has been deprecated, and is scheduled to be removed. Use imageio's imwrite instead.

    Now for the code. There are a few ways of doing this. I'll move from fewest modifications to most modifications.

    Fix up code

    I'm just going to make some changes to your program, to make it easier to modify later and so that it won't break in the future:

    import pypylon
    from imageio import imwrite
    
    available_cameras = pypylon.factory.find_devices()
    cam = pypylon.factory.create_device(available_cameras[0])
    cam.open()
    
    cam.properties['AcquisitionFrameRateEnable'] = True
    cam.properties['AcquisitionFrameRate'] = 1000
    cam.properties['ExposureTime'] = 1000
    
    for count, image in enumerate(cam.grab_images(30)):
        filename = str('I:/{}.png'.format(count))
        imwrite(filename, image)
    

    Future modifications will be based on this. I recommend looking up what some of these do, like 'ExposureTime', since it looks like you blindly copied these from the example.

    Buffer

    In order to store these images in a buffer, we can simply read them all straight away by converting the iterable into a tuple (a read-only list). Once we're done with the buffer we can delete it to free up some memory.

    import pypylon
    from imageio import imwrite
    
    available_cameras = pypylon.factory.find_devices()
    cam = pypylon.factory.create_device(available_cameras[0])
    cam.open()
    
    cam.properties['AcquisitionFrameRateEnable'] = True
    cam.properties['AcquisitionFrameRate'] = 1000
    cam.properties['ExposureTime'] = 1000
    
    buffer = tuple(cam.grab_images(30))
    for count, image in enumerate(buffer):
        filename = str('I:/{}.png'.format(count))
        imwrite(filename, image)
    del buffer
    

    Asynchronous saving

    I need to do something with multiprocessing. Oh, look, there's a Python module called multiprocessing. I've never used this before, but by reading it I can create this:

    import pypylon
    from imageio import imwrite
    from multiprocessing import Pool
    
    def save_image(pair):
        count, image = pair  # pair is actually two values
        filename = str('I:/{}.png'.format(count))
        imwrite(filename, image)
    
    if __name__ == "__main__":
        available_cameras = pypylon.factory.find_devices()
        cam = pypylon.factory.create_device(available_cameras[0])
        cam.open()
    
        cam.properties['AcquisitionFrameRateEnable'] = True
        cam.properties['AcquisitionFrameRate'] = 1000
        cam.properties['ExposureTime'] = 1000
    
        with Pool(30) as p:  # One for each image
            p.map(save_image, enumerate(cam.grab_images(30)))
    

    This solution is the one you asked for in the title. The most cryptic part of this is pair. This is there because each item generated by enumerate is a tuple of the form (i, image). This is only one argument, so it's passed to save_image as one argument. We need to expand that into the two variables count and image so that the rest of the code will work, which is what the first line of the function achieves.

    You might also have noticed if __name__ == "__main__":. This makes sure that the code only runs when the program is imported as a module, which is used internally by multiprocess to find the save_image function.

    I hope this does what you wanted. If you want clarification, feel free to post a comment. If you have a separate question, please ask another question.