[{"id":23244,"web_url":"https://patchwork.libcamera.org/comment/23244/","msgid":"<YpSU9vLGIPgnIScs@pendragon.ideasonboard.com>","date":"2022-05-30T09:57:10","subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nThank you for the patch.\n\nOn Fri, May 27, 2022 at 05:44:44PM +0300, Tomi Valkeinen wrote:\n> Add an example to showcase the more-or-less minimal capture case.\n> \n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> ---\n>  src/py/examples/simple-capture.py | 159 ++++++++++++++++++++++++++++++\n>  1 file changed, 159 insertions(+)\n>  create mode 100755 src/py/examples/simple-capture.py\n> \n> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py\n> new file mode 100755\n> index 00000000..4b0c3f0c\n> --- /dev/null\n> +++ b/src/py/examples/simple-capture.py\n> @@ -0,0 +1,159 @@\n> +#!/usr/bin/env python3\n> +\n> +# SPDX-License-Identifier: BSD-3-Clause\n> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> +\n> +# A simple minimal capture example showing:\n> +# - How to setup the camera\n> +# - Capture frames in a blocking manner\n> +# - Memory map the frames\n> +# - How to stop the camera\n> +#\n> +# This simple example is, in many ways, too simple. The purpose of the example\n> +# is to introduce the concepts. A more realistic example is given in\n> +# simple-continuous-capture.py.\n> +\n> +import argparse\n> +import binascii\n> +import libcamera as libcam\n> +import libcamera.utils\n> +import sys\n> +\n> +\n> +def main():\n> +    parser = argparse.ArgumentParser()\n> +    parser.add_argument('-c', '--camera', type=str, default='1',\n> +                        help='Camera index number (starting from 1) or part of the name')\n> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')\n> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n> +    args = parser.parse_args()\n> +\n> +    cm = libcam.CameraManager.singleton()\n> +\n> +    try:\n> +        if args.camera.isnumeric():\n> +            cam_idx = int(args.camera)\n> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))\n> +        else:\n> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))\n> +    except Exception:\n> +        print(f'Failed to find camera \"{args.camera}\"')\n> +        return -1\n> +\n> +    # Acquire the camera for our use\n> +\n> +    ret = cam.acquire()\n> +    assert ret == 0\n> +\n> +    # Configure the camera\n> +\n> +    cam_config = cam.generate_configuration([libcam.StreamRole.Viewfinder])\n> +\n> +    stream_config = cam_config.at(0)\n> +\n> +    if args.format:\n> +        fmt = libcam.PixelFormat(args.format)\n> +        stream_config.pixel_format = fmt\n> +\n> +    if args.size:\n> +        w, h = [int(v) for v in args.size.split('x')]\n> +        stream_config.size = libcam.Size(w, h)\n> +\n> +    ret = cam.configure(cam_config)\n> +    assert ret == 0\n> +\n> +    stream = stream_config.stream\n> +\n> +    # Allocate the buffers for capture\n> +\n> +    allocator = libcam.FrameBufferAllocator(cam)\n> +    ret = allocator.allocate(stream)\n> +    assert ret > 0\n> +\n> +    num_bufs = len(allocator.buffers(stream))\n> +\n> +    print(f'Capturing {num_bufs} frames with {stream_config}')\n> +\n> +    # Create the requests and assign a buffer for each request\n> +\n> +    reqs = []\n> +    for i in range(num_bufs):\n> +        # Use the buffer index as the cookie\n> +        req = cam.create_request(i)\n> +\n> +        buffer = allocator.buffers(stream)[i]\n> +        ret = req.add_buffer(stream, buffer)\n> +        assert ret == 0\n> +\n> +        reqs.append(req)\n> +\n> +    # Start the camera\n> +\n> +    ret = cam.start()\n> +    assert ret == 0\n> +\n> +    # Queue the requests to the camera\n> +\n> +    for req in reqs:\n> +        ret = cam.queue_request(req)\n> +        assert ret == 0\n> +\n> +    # Wait until the requests are finished\n> +\n> +    reqs = []\n> +\n> +    while True:\n> +        # cm.read_event() blocks until there is an event\n> +        cm.read_event()\n\nThis is the only example I'm not sure about, as I'd like to avoid\nencouraging people to not use an event loop. I'm tempted to drop this\none and rename simple-continuous-capture.py to simple-capture.py.\n\n> +\n> +        # Get all the ready requests\n> +        ready_reqs = cm.get_ready_requests()\n> +\n> +        reqs += ready_reqs\n> +\n> +        if len(reqs) >= num_bufs:\n> +            break\n> +\n> +    # Process the captured frames\n> +\n> +    for i, req in enumerate(reqs):\n> +        assert i == req.cookie\n> +\n> +        buffers = req.buffers\n> +\n> +        # A ready Request could contain multiple buffers if multiple streams\n> +        # were being used. Here we know we only have a single stream,\n> +        # and we use next(iter()) to get the first and only buffer.\n> +\n> +        assert len(buffers) == 1\n> +\n> +        stream, fb = next(iter(buffers.items()))\n> +\n> +        # Use MappedFrameBuffer to access the pixel data with CPU. We calculate\n> +        # the crc for each plane.\n> +\n> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n> +\n> +        meta = fb.metadata\n> +\n> +        print('seq {}, bytes {}, CRCs {}'\n> +              .format(meta.sequence,\n> +                      '/'.join([str(p.bytes_used) for p in meta.planes]),\n> +                      crcs))\n> +\n> +    # Stop the camera\n> +\n> +    ret = cam.stop()\n> +    assert ret == 0\n> +\n> +    # Release the camera\n> +\n> +    ret = cam.release()\n> +    assert ret == 0\n> +\n> +    return 0\n> +\n> +\n> +if __name__ == '__main__':\n> +    sys.exit(main())","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B974ABD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 09:57:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7C8A965633;\n\tMon, 30 May 2022 11:57:14 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6C56C6040B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 11:57:13 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(lmontsouris-659-1-41-236.w92-154.abo.wanadoo.fr [92.154.76.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id ED9D56BD;\n\tMon, 30 May 2022 11:57:12 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653904634;\n\tbh=bNRywm+XrpFvhIRiKtBRjAGkmf348VwWBn7dzk3/lBE=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=pY/6DIj8dW8tfwMJc/qQwICjttL49jZqDC3t+ZtcnXUjE2cNoJxz4w3Yyt42x/Ee+\n\tWHHcobP3uUeU2ojtpbjvdbB3J0lj0oTbPmJQfBuxqaXBKMJD2w8TSZ0ZOUNMeWEHob\n\tEiHeIosJE7bcXczPidDVJdeD2237S0HMM/7bqiR0M0eYpe//h/PnWvcqHQPEvy1Rco\n\tSFcV6VFvfm2L7f0O9NwjMFXCKdJF1uodgH//RmMLfFoKhw08bNtUESsm9dHMpu5V7r\n\tCiERPBSuL9m1ep7EZ2ZKXkv+LUhRM4uz/+HXy04WHZSeJBdG47qL5/4pisln3NvDyy\n\tvlWQvijOuQMFg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653904633;\n\tbh=bNRywm+XrpFvhIRiKtBRjAGkmf348VwWBn7dzk3/lBE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=rB9VctQH13Es7WtRVFoRSz1v5MxAckhDQ0gb0tp+3ZUsUCWyiP9SkDArK37JDjKEO\n\t5YJ478RXWaZ0cEfCoXtKSdjpzKejscUBXc1wtmKxUWhedRwaxTR/m+4UKlIxT6P9QN\n\t8HuBHDjX7KiDqJt8FRowMklcWxJHHRPeNu4Udc+I="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"rB9VctQH\"; dkim-atps=neutral","Date":"Mon, 30 May 2022 12:57:10 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<YpSU9vLGIPgnIScs@pendragon.ideasonboard.com>","References":"<20220527144447.94891-1-tomi.valkeinen@ideasonboard.com>\n\t<20220527144447.94891-28-tomi.valkeinen@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220527144447.94891-28-tomi.valkeinen@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23246,"web_url":"https://patchwork.libcamera.org/comment/23246/","msgid":"<d124f92a-98f8-b096-9ffb-f9a20ed30d6a@ideasonboard.com>","date":"2022-05-30T10:36:59","subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 30/05/2022 12:57, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> Thank you for the patch.\n> \n> On Fri, May 27, 2022 at 05:44:44PM +0300, Tomi Valkeinen wrote:\n>> Add an example to showcase the more-or-less minimal capture case.\n>>\n>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> ---\n>>   src/py/examples/simple-capture.py | 159 ++++++++++++++++++++++++++++++\n>>   1 file changed, 159 insertions(+)\n>>   create mode 100755 src/py/examples/simple-capture.py\n>>\n>> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py\n>> new file mode 100755\n>> index 00000000..4b0c3f0c\n>> --- /dev/null\n>> +++ b/src/py/examples/simple-capture.py\n>> @@ -0,0 +1,159 @@\n>> +#!/usr/bin/env python3\n>> +\n>> +# SPDX-License-Identifier: BSD-3-Clause\n>> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> +\n>> +# A simple minimal capture example showing:\n>> +# - How to setup the camera\n>> +# - Capture frames in a blocking manner\n>> +# - Memory map the frames\n>> +# - How to stop the camera\n>> +#\n>> +# This simple example is, in many ways, too simple. The purpose of the example\n>> +# is to introduce the concepts. A more realistic example is given in\n>> +# simple-continuous-capture.py.\n>> +\n>> +import argparse\n>> +import binascii\n>> +import libcamera as libcam\n>> +import libcamera.utils\n>> +import sys\n>> +\n>> +\n>> +def main():\n>> +    parser = argparse.ArgumentParser()\n>> +    parser.add_argument('-c', '--camera', type=str, default='1',\n>> +                        help='Camera index number (starting from 1) or part of the name')\n>> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')\n>> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n>> +    args = parser.parse_args()\n>> +\n>> +    cm = libcam.CameraManager.singleton()\n>> +\n>> +    try:\n>> +        if args.camera.isnumeric():\n>> +            cam_idx = int(args.camera)\n>> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))\n>> +        else:\n>> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))\n>> +    except Exception:\n>> +        print(f'Failed to find camera \"{args.camera}\"')\n>> +        return -1\n>> +\n>> +    # Acquire the camera for our use\n>> +\n>> +    ret = cam.acquire()\n>> +    assert ret == 0\n>> +\n>> +    # Configure the camera\n>> +\n>> +    cam_config = cam.generate_configuration([libcam.StreamRole.Viewfinder])\n>> +\n>> +    stream_config = cam_config.at(0)\n>> +\n>> +    if args.format:\n>> +        fmt = libcam.PixelFormat(args.format)\n>> +        stream_config.pixel_format = fmt\n>> +\n>> +    if args.size:\n>> +        w, h = [int(v) for v in args.size.split('x')]\n>> +        stream_config.size = libcam.Size(w, h)\n>> +\n>> +    ret = cam.configure(cam_config)\n>> +    assert ret == 0\n>> +\n>> +    stream = stream_config.stream\n>> +\n>> +    # Allocate the buffers for capture\n>> +\n>> +    allocator = libcam.FrameBufferAllocator(cam)\n>> +    ret = allocator.allocate(stream)\n>> +    assert ret > 0\n>> +\n>> +    num_bufs = len(allocator.buffers(stream))\n>> +\n>> +    print(f'Capturing {num_bufs} frames with {stream_config}')\n>> +\n>> +    # Create the requests and assign a buffer for each request\n>> +\n>> +    reqs = []\n>> +    for i in range(num_bufs):\n>> +        # Use the buffer index as the cookie\n>> +        req = cam.create_request(i)\n>> +\n>> +        buffer = allocator.buffers(stream)[i]\n>> +        ret = req.add_buffer(stream, buffer)\n>> +        assert ret == 0\n>> +\n>> +        reqs.append(req)\n>> +\n>> +    # Start the camera\n>> +\n>> +    ret = cam.start()\n>> +    assert ret == 0\n>> +\n>> +    # Queue the requests to the camera\n>> +\n>> +    for req in reqs:\n>> +        ret = cam.queue_request(req)\n>> +        assert ret == 0\n>> +\n>> +    # Wait until the requests are finished\n>> +\n>> +    reqs = []\n>> +\n>> +    while True:\n>> +        # cm.read_event() blocks until there is an event\n>> +        cm.read_event()\n> \n> This is the only example I'm not sure about, as I'd like to avoid\n> encouraging people to not use an event loop. I'm tempted to drop this\n> one and rename simple-continuous-capture.py to simple-capture.py.\n\nI can change this from a loop blocking in cm.read_event() to a loop \nblocking in select, as it's just a few lines more. But I don't think it \nmaterially changes the example.\n\nI really like the simple-capture.py, as it's an example in a single \nfunction, so you just read it from top to bottom and it makes sense. In \nsimple-continuous-capture.py you need to jump back and forth the \nfunctions a bit.\n\nAlthough I could also add a few more lines of code to add reuse & \nrequeuing of the requests and interrupt via keypress, and still keep it \nin a single function.\n\n  Tomi","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 6B003BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 10:37:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A19C065633;\n\tMon, 30 May 2022 12:37:04 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5700F60411\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 12:37:03 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7A93D6DF;\n\tMon, 30 May 2022 12:37:02 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653907024;\n\tbh=eu04p5gx8ydwWmH5a5VMmnykIW3a7LAl9ohAB6rCj1s=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=hu7I76ezfRbZuvk7TTdyE7B0vGWZca7QorAFfU67cQml1ssOApK13KfB+E9+R7rYT\n\tidmjVWMumG8ygbTidd8Fqp1F8HZtui9rG0wINQiZxzZwmTD9W2PYyGsrhZOJjpdYiy\n\twkJp8zYKnJZNnU0v/6AqYJ94eDxdJpO+a6B1FgCx5l0A/vn1GzHpVWWrp+Pxs0eVe8\n\t31yKHvpuNX66k6PIuU7givlYMlrlfWDN3k01inSrJkXRjhE2DxQmvROEVx2dX2fokQ\n\t1QQ1qtTRTEnY4HlbkaHxtrax4fMel7H4O7Vwf8fgTJvIRKEwK14zbmfSP/VbO/Utvk\n\tNwCxwTk/8jf6Q==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653907022;\n\tbh=eu04p5gx8ydwWmH5a5VMmnykIW3a7LAl9ohAB6rCj1s=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=a/SIqhX1Bs6BQW8RTX7fWEKgqxH9BW8Nt/a7kabovIEK7lICK2x15bvCe8DdvsVo1\n\tsUx55Vz/3CfnaEQ2wnak4pOi59+xEnAqtJ58JXNQnaYbRh8LbH0S7tnvHfjf6BgkyP\n\toFWmnc7WOu2qAdz6pUYEZsEmyymIenrgEi7b2vc0="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"a/SIqhX1\"; dkim-atps=neutral","Message-ID":"<d124f92a-98f8-b096-9ffb-f9a20ed30d6a@ideasonboard.com>","Date":"Mon, 30 May 2022 13:36:59 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220527144447.94891-1-tomi.valkeinen@ideasonboard.com>\n\t<20220527144447.94891-28-tomi.valkeinen@ideasonboard.com>\n\t<YpSU9vLGIPgnIScs@pendragon.ideasonboard.com>","In-Reply-To":"<YpSU9vLGIPgnIScs@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23249,"web_url":"https://patchwork.libcamera.org/comment/23249/","msgid":"<f5d67ee0-fa96-3cbe-9861-290221dd839c@ideasonboard.com>","date":"2022-05-30T10:45:31","subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 30/05/2022 13:36, Tomi Valkeinen wrote:\n> On 30/05/2022 12:57, Laurent Pinchart wrote:\n>> Hi Tomi,\n>>\n>> Thank you for the patch.\n>>\n>> On Fri, May 27, 2022 at 05:44:44PM +0300, Tomi Valkeinen wrote:\n>>> Add an example to showcase the more-or-less minimal capture case.\n>>>\n>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>>> ---\n>>>   src/py/examples/simple-capture.py | 159 ++++++++++++++++++++++++++++++\n>>>   1 file changed, 159 insertions(+)\n>>>   create mode 100755 src/py/examples/simple-capture.py\n>>>\n>>> diff --git a/src/py/examples/simple-capture.py \n>>> b/src/py/examples/simple-capture.py\n>>> new file mode 100755\n>>> index 00000000..4b0c3f0c\n>>> --- /dev/null\n>>> +++ b/src/py/examples/simple-capture.py\n>>> @@ -0,0 +1,159 @@\n>>> +#!/usr/bin/env python3\n>>> +\n>>> +# SPDX-License-Identifier: BSD-3-Clause\n>>> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>>> +\n>>> +# A simple minimal capture example showing:\n>>> +# - How to setup the camera\n>>> +# - Capture frames in a blocking manner\n>>> +# - Memory map the frames\n>>> +# - How to stop the camera\n>>> +#\n>>> +# This simple example is, in many ways, too simple. The purpose of \n>>> the example\n>>> +# is to introduce the concepts. A more realistic example is given in\n>>> +# simple-continuous-capture.py.\n>>> +\n>>> +import argparse\n>>> +import binascii\n>>> +import libcamera as libcam\n>>> +import libcamera.utils\n>>> +import sys\n>>> +\n>>> +\n>>> +def main():\n>>> +    parser = argparse.ArgumentParser()\n>>> +    parser.add_argument('-c', '--camera', type=str, default='1',\n>>> +                        help='Camera index number (starting from 1) \n>>> or part of the name')\n>>> +    parser.add_argument('-f', '--format', type=str, help='Pixel \n>>> format')\n>>> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n>>> +    args = parser.parse_args()\n>>> +\n>>> +    cm = libcam.CameraManager.singleton()\n>>> +\n>>> +    try:\n>>> +        if args.camera.isnumeric():\n>>> +            cam_idx = int(args.camera)\n>>> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i \n>>> + 1 == cam_idx))\n>>> +        else:\n>>> +            cam = next((cam for cam in cm.cameras if args.camera in \n>>> cam.id))\n>>> +    except Exception:\n>>> +        print(f'Failed to find camera \"{args.camera}\"')\n>>> +        return -1\n>>> +\n>>> +    # Acquire the camera for our use\n>>> +\n>>> +    ret = cam.acquire()\n>>> +    assert ret == 0\n>>> +\n>>> +    # Configure the camera\n>>> +\n>>> +    cam_config = \n>>> cam.generate_configuration([libcam.StreamRole.Viewfinder])\n>>> +\n>>> +    stream_config = cam_config.at(0)\n>>> +\n>>> +    if args.format:\n>>> +        fmt = libcam.PixelFormat(args.format)\n>>> +        stream_config.pixel_format = fmt\n>>> +\n>>> +    if args.size:\n>>> +        w, h = [int(v) for v in args.size.split('x')]\n>>> +        stream_config.size = libcam.Size(w, h)\n>>> +\n>>> +    ret = cam.configure(cam_config)\n>>> +    assert ret == 0\n>>> +\n>>> +    stream = stream_config.stream\n>>> +\n>>> +    # Allocate the buffers for capture\n>>> +\n>>> +    allocator = libcam.FrameBufferAllocator(cam)\n>>> +    ret = allocator.allocate(stream)\n>>> +    assert ret > 0\n>>> +\n>>> +    num_bufs = len(allocator.buffers(stream))\n>>> +\n>>> +    print(f'Capturing {num_bufs} frames with {stream_config}')\n>>> +\n>>> +    # Create the requests and assign a buffer for each request\n>>> +\n>>> +    reqs = []\n>>> +    for i in range(num_bufs):\n>>> +        # Use the buffer index as the cookie\n>>> +        req = cam.create_request(i)\n>>> +\n>>> +        buffer = allocator.buffers(stream)[i]\n>>> +        ret = req.add_buffer(stream, buffer)\n>>> +        assert ret == 0\n>>> +\n>>> +        reqs.append(req)\n>>> +\n>>> +    # Start the camera\n>>> +\n>>> +    ret = cam.start()\n>>> +    assert ret == 0\n>>> +\n>>> +    # Queue the requests to the camera\n>>> +\n>>> +    for req in reqs:\n>>> +        ret = cam.queue_request(req)\n>>> +        assert ret == 0\n>>> +\n>>> +    # Wait until the requests are finished\n>>> +\n>>> +    reqs = []\n>>> +\n>>> +    while True:\n>>> +        # cm.read_event() blocks until there is an event\n>>> +        cm.read_event()\n>>\n>> This is the only example I'm not sure about, as I'd like to avoid\n>> encouraging people to not use an event loop. I'm tempted to drop this\n>> one and rename simple-continuous-capture.py to simple-capture.py.\n> \n> I can change this from a loop blocking in cm.read_event() to a loop \n> blocking in select, as it's just a few lines more. But I don't think it \n> materially changes the example.\n> \n> I really like the simple-capture.py, as it's an example in a single \n> function, so you just read it from top to bottom and it makes sense. In \n> simple-continuous-capture.py you need to jump back and forth the \n> functions a bit.\n> \n> Although I could also add a few more lines of code to add reuse & \n> requeuing of the requests and interrupt via keypress, and still keep it \n> in a single function.\n\nActually, I don't know if that makes sense. If you want to write a short \nscript that captures, say, 50 frames from a camera, why would you \nimplement an event loop instead of just a blocking loop until you got \nyour frames? I don't see anything wrong with that.\n\n  Tomi","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 858A9BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 10:45:36 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CED6C65633;\n\tMon, 30 May 2022 12:45:35 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2A31C60411\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 12:45:35 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 206FCE57;\n\tMon, 30 May 2022 12:45:34 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653907535;\n\tbh=uxJ3DUhFLdRaSvyU776PhlcZLbZF1UBZuNBdq0MDEps=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=F27asEJw7ZvP/EXesu7zIzGR1K2aNfEwiWBVW8KVMMH25osS1R5pavAOQ3TmJcZUK\n\txP4zaMxUXwZKpZOrxTyB15vecIacD7SOplhLdFqHkSxwa2thr/3vh8RBQjHlvA3wEe\n\tA5XWKl3r/R4iBjyrwil9v525QphpckbpooTQHpJVOQYsMj+slE0WV+8TsSbSKar8h3\n\tM79/l7sDkcKn8U1euj/prY2njQalEF3i6Rl9eGV7Y4UAgAQdVRRG+f7Zovnxo6F8QR\n\tg1qMeLozwcElDtgItQbW8W7Y4PsYe7RmCnpZb8d+AzkK4T4nSpNH2wtHYsM6gqb95V\n\tqmeE/m65rVBcA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653907534;\n\tbh=uxJ3DUhFLdRaSvyU776PhlcZLbZF1UBZuNBdq0MDEps=;\n\th=Date:Subject:From:To:Cc:References:In-Reply-To:From;\n\tb=DD4Jomyo//MbzrMNKOBNljhwenKJ/aHiyAtMSuO+CrVk5yDA/LR+9M4K1z6qSDrNN\n\tzHYRNuSZp3MAaPJVn+mBDnuQ/2FRTg/Hb5rkq3dAcf/kQBYIscmb7FDg+OUBVQbRbN\n\ty34SHp3p7/zXC1Wk1Z82RBWs7Pcd5iWjSGEVyWog="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"DD4Jomyo\"; dkim-atps=neutral","Message-ID":"<f5d67ee0-fa96-3cbe-9861-290221dd839c@ideasonboard.com>","Date":"Mon, 30 May 2022 13:45:31 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220527144447.94891-1-tomi.valkeinen@ideasonboard.com>\n\t<20220527144447.94891-28-tomi.valkeinen@ideasonboard.com>\n\t<YpSU9vLGIPgnIScs@pendragon.ideasonboard.com>\n\t<d124f92a-98f8-b096-9ffb-f9a20ed30d6a@ideasonboard.com>","In-Reply-To":"<d124f92a-98f8-b096-9ffb-f9a20ed30d6a@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v3 27/30] py: examples: Add\n\tsimple-capture.py","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]