[libcamera-devel] [PATCH v3 27/30] py: examples: Add simple-capture.py

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon May 30 11:57:10 CEST 2022


Hi Tomi,

Thank you for the patch.

On Fri, May 27, 2022 at 05:44:44PM +0300, Tomi Valkeinen wrote:
> Add an example to showcase the more-or-less minimal capture case.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> ---
>  src/py/examples/simple-capture.py | 159 ++++++++++++++++++++++++++++++
>  1 file changed, 159 insertions(+)
>  create mode 100755 src/py/examples/simple-capture.py
> 
> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py
> new file mode 100755
> index 00000000..4b0c3f0c
> --- /dev/null
> +++ b/src/py/examples/simple-capture.py
> @@ -0,0 +1,159 @@
> +#!/usr/bin/env python3
> +
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> +
> +# A simple minimal capture example showing:
> +# - How to setup the camera
> +# - Capture frames in a blocking manner
> +# - Memory map the frames
> +# - How to stop the camera
> +#
> +# This simple example is, in many ways, too simple. The purpose of the example
> +# is to introduce the concepts. A more realistic example is given in
> +# simple-continuous-capture.py.
> +
> +import argparse
> +import binascii
> +import libcamera as libcam
> +import libcamera.utils
> +import sys
> +
> +
> +def main():
> +    parser = argparse.ArgumentParser()
> +    parser.add_argument('-c', '--camera', type=str, default='1',
> +                        help='Camera index number (starting from 1) or part of the name')
> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')
> +    parser.add_argument('-s', '--size', type=str, help='Size ("WxH")')
> +    args = parser.parse_args()
> +
> +    cm = libcam.CameraManager.singleton()
> +
> +    try:
> +        if args.camera.isnumeric():
> +            cam_idx = int(args.camera)
> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))
> +        else:
> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))
> +    except Exception:
> +        print(f'Failed to find camera "{args.camera}"')
> +        return -1
> +
> +    # Acquire the camera for our use
> +
> +    ret = cam.acquire()
> +    assert ret == 0
> +
> +    # Configure the camera
> +
> +    cam_config = cam.generate_configuration([libcam.StreamRole.Viewfinder])
> +
> +    stream_config = cam_config.at(0)
> +
> +    if args.format:
> +        fmt = libcam.PixelFormat(args.format)
> +        stream_config.pixel_format = fmt
> +
> +    if args.size:
> +        w, h = [int(v) for v in args.size.split('x')]
> +        stream_config.size = libcam.Size(w, h)
> +
> +    ret = cam.configure(cam_config)
> +    assert ret == 0
> +
> +    stream = stream_config.stream
> +
> +    # Allocate the buffers for capture
> +
> +    allocator = libcam.FrameBufferAllocator(cam)
> +    ret = allocator.allocate(stream)
> +    assert ret > 0
> +
> +    num_bufs = len(allocator.buffers(stream))
> +
> +    print(f'Capturing {num_bufs} frames with {stream_config}')
> +
> +    # Create the requests and assign a buffer for each request
> +
> +    reqs = []
> +    for i in range(num_bufs):
> +        # Use the buffer index as the cookie
> +        req = cam.create_request(i)
> +
> +        buffer = allocator.buffers(stream)[i]
> +        ret = req.add_buffer(stream, buffer)
> +        assert ret == 0
> +
> +        reqs.append(req)
> +
> +    # Start the camera
> +
> +    ret = cam.start()
> +    assert ret == 0
> +
> +    # Queue the requests to the camera
> +
> +    for req in reqs:
> +        ret = cam.queue_request(req)
> +        assert ret == 0
> +
> +    # Wait until the requests are finished
> +
> +    reqs = []
> +
> +    while True:
> +        # cm.read_event() blocks until there is an event
> +        cm.read_event()

This is the only example I'm not sure about, as I'd like to avoid
encouraging people to not use an event loop. I'm tempted to drop this
one and rename simple-continuous-capture.py to simple-capture.py.

> +
> +        # Get all the ready requests
> +        ready_reqs = cm.get_ready_requests()
> +
> +        reqs += ready_reqs
> +
> +        if len(reqs) >= num_bufs:
> +            break
> +
> +    # Process the captured frames
> +
> +    for i, req in enumerate(reqs):
> +        assert i == req.cookie
> +
> +        buffers = req.buffers
> +
> +        # A ready Request could contain multiple buffers if multiple streams
> +        # were being used. Here we know we only have a single stream,
> +        # and we use next(iter()) to get the first and only buffer.
> +
> +        assert len(buffers) == 1
> +
> +        stream, fb = next(iter(buffers.items()))
> +
> +        # Use MappedFrameBuffer to access the pixel data with CPU. We calculate
> +        # the crc for each plane.
> +
> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:
> +            crcs = [binascii.crc32(p) for p in mfb.planes]
> +
> +        meta = fb.metadata
> +
> +        print('seq {}, bytes {}, CRCs {}'
> +              .format(meta.sequence,
> +                      '/'.join([str(p.bytes_used) for p in meta.planes]),
> +                      crcs))
> +
> +    # Stop the camera
> +
> +    ret = cam.stop()
> +    assert ret == 0
> +
> +    # Release the camera
> +
> +    ret = cam.release()
> +    assert ret == 0
> +
> +    return 0
> +
> +
> +if __name__ == '__main__':
> +    sys.exit(main())

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list