Show a patch.

GET /api/patches/10099/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 10099,
    "url": "https://patchwork.libcamera.org/api/patches/10099/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/10099/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20201019125156.26751-6-david.plowman@raspberrypi.com>",
    "date": "2020-10-19T12:51:56",
    "name": "[libcamera-devel,v4,5/5] libcamera: pipeline: raspberrypi: Implementation of digital zoom",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "ee745fc761ac2e3fd47e47074147f6071bc7b717",
    "submitter": {
        "id": 42,
        "url": "https://patchwork.libcamera.org/api/people/42/?format=api",
        "name": "David Plowman",
        "email": "david.plowman@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/10099/mbox/",
    "series": [
        {
            "id": 1384,
            "url": "https://patchwork.libcamera.org/api/series/1384/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1384",
            "date": "2020-10-19T12:51:51",
            "name": "Digital zoom",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/1384/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/10099/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/10099/checks/",
    "tags": {},
    "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 4DFFCBDB1F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 19 Oct 2020 12:52:12 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F197360CE6;\n\tMon, 19 Oct 2020 14:52:11 +0200 (CEST)",
            "from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com\n\t[IPv6:2a00:1450:4864:20::42a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 728CD613AD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 19 Oct 2020 14:52:09 +0200 (CEST)",
            "by mail-wr1-x42a.google.com with SMTP id j7so7197855wrt.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 19 Oct 2020 05:52:09 -0700 (PDT)",
            "from pi4-davidp.pitowers.org\n\t([2a00:1098:3142:14:1ce1:9965:4328:89c4])\n\tby smtp.gmail.com with ESMTPSA id\n\tq6sm16634335wma.0.2020.10.19.05.52.08\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 19 Oct 2020 05:52:08 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"Ua6sUqmZ\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=Da3jfoEzqJ/NANYN3bEr5iKNEQ2R+Mn1bTNJdEXTVFk=;\n\tb=Ua6sUqmZxZ0EY/shdVdYAKjlwZo7YFiERPfEhkpRVL6T/iFOUxn51/vpNW0GULdYp+\n\tw+JVk8xAYIVlnYjo+n4GIVjd2E9ALUeEG6OFBqPTAjsKUOGcwP9pzFt9Hb5sfZXc4eUz\n\tpdhr7iVkUQMaozVL4b3x2yB3589q62sjWhiwORAWi+/2WKDKxFCmoLgN+c1QPAzXPUA3\n\tWWh6Kh513jW8K83AfHK0NpikK3r+0VnKmJm0XJYhesE/R+8eTWZ8BUSKQqoq/KgsdxDw\n\topgISN1NPqU43PTEmrHI3Ad4mpgzHZ/qUsTheqHjoIty/AcBbjOOn9Z+IZB8ZD1gae3H\n\ttzgw==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=Da3jfoEzqJ/NANYN3bEr5iKNEQ2R+Mn1bTNJdEXTVFk=;\n\tb=olp4htdYahCmyXFGqZ27FM1KIADtponD4mguQuD5Pcp6wa0sWKCZAKL54C1KBkkdWS\n\tui2EuLDgiwqhrmYInPjXeRYU6C3LgXkfIaUFajcVQZicbLqLbep0l4ConQmwGrRkdLYb\n\tOuBpAnGuIzCUJF9C0YnfMCitxwug2pJVFx5J1UizTJxNonxOVUiXdika/M/EWPb9HHKs\n\tGZfiHKftsK12IBdKlIDDcMODZo+ZupzsjwPaqeN2J6YyeswajflRhCgVpE+XP99+he6J\n\tLgIupfQRfYvc/c8C6uJsi9MgZIDW7jZUdBypULS2pMEg/q1RdKFlM2QJaO6euNJ270DN\n\tm4zA==",
        "X-Gm-Message-State": "AOAM53189c4vmqp2Tpb4DjKnTWRLnsPM/YJu5k5Dz/jQFebikqQI+Ffc\n\teCGs+jZgrFi1Eug3W2+SDgkuX/1+IggJVQ==",
        "X-Google-Smtp-Source": "ABdhPJzU4lXl9D53GGPW30F3rsVoVSnIb6R+CmUwkBDvKJZo6fBanDzOyQOtA3SL8MCpa9HlbUyENw==",
        "X-Received": "by 2002:a5d:424e:: with SMTP id\n\ts14mr21392702wrr.149.1603111928827; \n\tMon, 19 Oct 2020 05:52:08 -0700 (PDT)",
        "From": "David Plowman <david.plowman@raspberrypi.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Mon, 19 Oct 2020 13:51:56 +0100",
        "Message-Id": "<20201019125156.26751-6-david.plowman@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.20.1",
        "In-Reply-To": "<20201019125156.26751-1-david.plowman@raspberrypi.com>",
        "References": "<20201019125156.26751-1-david.plowman@raspberrypi.com>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [PATCH v4 5/5] libcamera: pipeline: raspberrypi:\n\tImplementation of digital zoom",
        "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>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "During configure() we update the ScalerCropMaximum to the correct\nvalue for this camera mode (which we also note internally) and work\nout the minimum crop size allowed by the ISP.\n\nWhenever a new ScalerCrop request is received we check it's valid and\napply it to the ISP V4L2 device. We also forward it to the IPA so that\nthe IPA can return the values used in the image metadata.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n include/libcamera/ipa/raspberrypi.h           |  1 +\n src/ipa/raspberrypi/raspberrypi.cpp           |  7 ++\n .../pipeline/raspberrypi/raspberrypi.cpp      | 70 +++++++++++++++----\n 3 files changed, 63 insertions(+), 15 deletions(-)",
    "diff": "diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h\nindex b3041591..bb4e1a8c 100644\n--- a/include/libcamera/ipa/raspberrypi.h\n+++ b/include/libcamera/ipa/raspberrypi.h\n@@ -60,6 +60,7 @@ static const ControlInfoMap Controls = {\n \t{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },\n \t{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },\n \t{ &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) },\n+\t{ &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },\n };\n \n } /* namespace RPi */\ndiff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\nindex 48a72dd2..e777f0b4 100644\n--- a/src/ipa/raspberrypi/raspberrypi.cpp\n+++ b/src/ipa/raspberrypi/raspberrypi.cpp\n@@ -699,6 +699,13 @@ void IPARPi::queueRequest(const ControlList &controls)\n \t\t\tbreak;\n \t\t}\n \n+\t\tcase controls::SCALER_CROP: {\n+\t\t\t/* Just copy the information back. */\n+\t\t\tRectangle crop = ctrl.second.get<Rectangle>();\n+\t\t\tlibcameraMetadata_.set(controls::ScalerCrop, crop);\n+\t\t\tbreak;\n+\t\t}\n+\n \t\tdefault:\n \t\t\tLOG(IPARPI, Warning)\n \t\t\t\t<< \"Ctrl \" << controls::controls.at(ctrl.first)->name()\ndiff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\nindex 26dbd257..43b860ae 100644\n--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n@@ -193,6 +193,11 @@ public:\n \tbool flipsAlterBayerOrder_;\n \tBayerFormat::Order nativeBayerOrder_;\n \n+\t/* For handling digital zoom. */\n+\tCameraSensorInfo sensorInfo_;\n+\tRectangle lastIspCrop_;\n+\tSize ispMinSize_;\n+\n \tunsigned int dropFrameCount_;\n \n private:\n@@ -677,26 +682,30 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)\n \t\treturn ret;\n \t}\n \n-\t/* Adjust aspect ratio by providing crops on the input image. */\n-\tRectangle crop{ 0, 0, sensorFormat.size };\n-\n-\tint ar = maxSize.height * sensorFormat.size.width - maxSize.width * sensorFormat.size.height;\n-\tif (ar > 0)\n-\t\tcrop.width = maxSize.width * sensorFormat.size.height / maxSize.height;\n-\telse if (ar < 0)\n-\t\tcrop.height = maxSize.height * sensorFormat.size.width / maxSize.width;\n+\t/* Figure out the smallest selection the ISP will allow. */\n+\tRectangle testCrop(0, 0, 1, 1);\n+\tdata->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop);\n+\tdata->ispMinSize_ = testCrop.size();\n \n-\tcrop.width &= ~1;\n-\tcrop.height &= ~1;\n+\t/* Adjust aspect ratio by providing crops on the input image. */\n+\tRectangle crop = sensorFormat.size.boundedToAspectRatio(maxSize).centeredTo(sensorFormat.size.center());\n+\tdata->lastIspCrop_ = crop;\n \n-\tcrop.x = (sensorFormat.size.width - crop.width) >> 1;\n-\tcrop.y = (sensorFormat.size.height - crop.height) >> 1;\n \tdata->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);\n \n \tret = data->configureIPA(config);\n \tif (ret)\n \t\tLOG(RPI, Error) << \"Failed to configure the IPA: \" << ret;\n \n+\t/*\n+\t * Update the ScalerCropMaximum to the correct value for this camera mode.\n+\t * For us, it's the same as the \"analogue crop\".\n+\t *\n+\t * \\todo Make this property the ScalerCrop maximum value when dynamic\n+\t * controls are available and set it at validate() time\n+\t */\n+\tdata->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop);\n+\n \treturn ret;\n }\n \n@@ -1147,8 +1156,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n \t\tipaConfig.data.push_back(static_cast<unsigned int>(lsTable_.fd()));\n \t}\n \n-\tCameraSensorInfo sensorInfo = {};\n-\tint ret = sensor_->sensorInfo(&sensorInfo);\n+\t/* We store the CameraSensorInfo for digital zoom calculations. */\n+\tint ret = sensor_->sensorInfo(&sensorInfo_);\n \tif (ret) {\n \t\tLOG(RPI, Error) << \"Failed to retrieve camera sensor info\";\n \t\treturn ret;\n@@ -1157,7 +1166,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n \t/* Ready the IPA - it must know about the sensor resolution. */\n \tIPAOperationData result;\n \n-\tipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig,\n+\tipa_->configure(sensorInfo_, streamConfig, entityControls, ipaConfig,\n \t\t\t&result);\n \n \tunsigned int resultIdx = 0;\n@@ -1588,6 +1597,37 @@ void RPiCameraData::tryRunPipeline()\n \t/* Take the first request from the queue and action the IPA. */\n \tRequest *request = requestQueue_.front();\n \n+\tif (request->controls().contains(controls::ScalerCrop)) {\n+\t\tRectangle crop = request->controls().get<Rectangle>(controls::ScalerCrop);\n+\n+\t\tif (crop.width && crop.height) {\n+\t\t\t/* First scale the crop from sensor native to camera mode pixels. */\n+\t\t\tcrop.translateBy(-sensorInfo_.analogCrop.x, -sensorInfo_.analogCrop.y);\n+\t\t\tcrop.rescaleTo(sensorInfo_.analogCrop.size(), sensorInfo_.outputSize);\n+\n+\t\t\t/*\n+\t\t\t * The crop that we set must be:\n+\t\t\t * 1. At least as big as ispMinSize_, once that's been\n+\t\t\t *    enlarged to the same aspect ratio.\n+\t\t\t * 2. With the same mid-point, if possible.\n+\t\t\t * 3. But it can't go outside the sensor area.\n+\t\t\t */\n+\t\t\tSize minSize = ispMinSize_.expandedToAspectRatio(crop.size());\n+\t\t\tSize size = crop.size().expandedTo(minSize);\n+\t\t\tcrop = size.centeredTo(crop.center()).clampedTo(Rectangle(sensorInfo_.outputSize));\n+\n+\t\t\tif (crop != lastIspCrop_)\n+\t\t\t\tisp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);\n+\t\t\tlastIspCrop_ = crop;\n+\t\t}\n+\n+\t\t/* We must scale the actual crop used back into native pixels. */\n+\t\tcrop = lastIspCrop_.rescaledTo(sensorInfo_.outputSize, sensorInfo_.analogCrop.size());\n+\t\tcrop.translateBy(sensorInfo_.analogCrop.x, sensorInfo_.analogCrop.y);\n+\n+\t\trequest->controls().set(controls::ScalerCrop, crop);\n+\t}\n+\n \t/*\n \t * Process all the user controls by the IPA. Once this is complete, we\n \t * queue the ISP output buffer listed in the request to start the HW\n",
    "prefixes": [
        "libcamera-devel",
        "v4",
        "5/5"
    ]
}