alliedvision / VimbaPython

Old Allied Vision Vimba Python API. The successor to this API is VmbPy
BSD 2-Clause "Simplified" License
93 stars 40 forks source link

Memory increasing #17

Closed rodrigo2019 closed 4 years ago

rodrigo2019 commented 4 years ago

Hello, for each frame captured inside the scope the memory increases, but I didn't find any documentation showing how to release this memory, for example:

with cams[0] as cam:
    while True:
        plc.write_by_name(PLC_CAMERA_STATUS, StatusCode.READY, pyads.PLCTYPE_INT)  # no info
        current_trigger_count = plc.read_by_name(PLC_IMAGE_TRIGGER, pyads.PLCTYPE_UINT)
        if current_trigger_count != plc_trigger_counter:
            frame = cam.get_frame(6000)
            frame.convert_pixel_format(PixelFormat.Bgr8)
            frame = frame.as_opencv_image()
            fname = locate_classify(frame, fname)
            cv2.imwrite(fname, cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY))
            logger.info("Image {} wrote.".format(fname))
            ...

But if I put the while True outside from the scope, forcing the code access the scope every loop, the memory doesnt increase, but the problem is that accessing the camera is slower and consumes lot more of cpu.

while True:
    with cams[0] as cam:
        plc.write_by_name(PLC_CAMERA_STATUS, StatusCode.READY, pyads.PLCTYPE_INT)  # no info
        current_trigger_count = plc.read_by_name(PLC_IMAGE_TRIGGER, pyads.PLCTYPE_UINT)
        if current_trigger_count != plc_trigger_counter:
            frame = cam.get_frame(6000)
            frame.convert_pixel_format(PixelFormat.Bgr8)
            frame = frame.as_opencv_image()
            fname = locate_classify(frame, fname)
            cv2.imwrite(fname, cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY))
            logger.info("Image {} wrote.".format(fname))
            ...

Could you guys help me?

NiklasKroeger-AlliedVision commented 4 years ago

Hi and sorry for the long wait.

I see that you are using cam.get_frame() to acquire a single image from the camera. The problem with that is, that VimbaPython will create a new image buffer for you for every call to that function. If you are doing these calls very often, many buffers will be allocated leading to the increase in memory you are experiencing.

I created a minimum working example of what you are seeing where I record 100 frames with get_frame() and have been monitoring the memory consumption of the python process executing this.

def main():
    with vimba.Vimba.get_instance():
        with get_camera(None) as cam:
            setup_camera(cam)

            for i in range(100):
                image = cam.get_frame()
                print(image)

I can also see the memory growing over time. However I also noticed, that periodically the memory would decrease to a smaller value. This is caused by the garbage collection of python executing and freeing unneeded memory.

With this in mind I simply tried running the garbage collection manually at the end of my for loop (in your case it would be at the end of the while True loop):

import gc

def main():
    with vimba.Vimba.get_instance():
        with get_camera(None) as cam:
            setup_camera(cam)

            for i in range(100):
                image = cam.get_frame()
                print(image)
                gc.collect()

With this I was able to keep the consumed memory of my python process low. So perhaps this is a workaround you could also try and see if it works for you.

This does however obviously come with a performance penalty. My suggestion would therefore be to see if instead you could restructure your application code to use asynchronous image acquisition. Here you would tell the camera to start streaming and use a callback function to be informed of new images. This has the advantage, that you are able to manually manage the frame buffers and could therefore circumvent the need for manually triggering a garbage collection in the first place. You would probably have to couple this with software triggering of the image acquisition to decide when to acquire new images from you r code as you are doing now with your get_frame() calls.

rodrigo2019 commented 4 years ago

@NiklasKroeger-AlliedVision thank you so much, I will try both solutions and give you a feedback.

btw nice OOP library.

rodrigo2019 commented 4 years ago

It worked using the garbage collector, thank you!