Patch Detail
Show a patch.
GET /api/patches/24523/?format=api
{ "id": 24523, "url": "https://patchwork.libcamera.org/api/patches/24523/?format=api", "web_url": "https://patchwork.libcamera.org/patch/24523/", "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": "<20250930122726.1837524-25-stefan.klug@ideasonboard.com>", "date": "2025-09-30T12:26:45", "name": "[v1,24/33] libcamera: rkisp1: Use vertex map to implement ScalerCrop", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "c8528dd8f901c1f86b28781791eeb38d59cddb22", "submitter": { "id": 184, "url": "https://patchwork.libcamera.org/api/people/184/?format=api", "name": "Stefan Klug", "email": "stefan.klug@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/24523/mbox/", "series": [ { "id": 5468, "url": "https://patchwork.libcamera.org/api/series/5468/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5468", "date": "2025-09-30T12:26:21", "name": "Full dewarper support on imx8mp", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5468/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/24523/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/24523/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 B9252C328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 30 Sep 2025 13:24:43 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ED8436B5F3;\n\tTue, 30 Sep 2025 15:24:42 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BE29562C35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Sep 2025 15:24:41 +0200 (CEST)", "from ideasonboard.com (unknown [94.31.94.171])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 5960B226;\n\tTue, 30 Sep 2025 15:23:13 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"A69g1Y2X\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1759238593;\n\tbh=IGSYJPNbfIhqPySXe9VsD4deKqwMpIi5GaiOcVc4Qvs=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=A69g1Y2XQ0smoiu9eHcCue3ItqxwDX6UPSx/HlP+t7CEvhfso2dig5oCgb/ZMhovb\n\t+Kgea+RIMPA+Iap5GShcncjjt5QGvzXD6FIH6MR0I+cf0sDIRL2ddTspq74SEus4dC\n\tUUEumrdInfXPIBlgLAgNDSQTuLYMvYg6KV2jXk20=", "From": "Stefan Klug <stefan.klug@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>", "Subject": "[PATCH v1 24/33] libcamera: rkisp1: Use vertex map to implement\n\tScalerCrop", "Date": "Tue, 30 Sep 2025 14:26:45 +0200", "Message-ID": "<20250930122726.1837524-25-stefan.klug@ideasonboard.com>", "X-Mailer": "git-send-email 2.48.1", "In-Reply-To": "<20250930122726.1837524-1-stefan.klug@ideasonboard.com>", "References": "<20250930122726.1837524-1-stefan.klug@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "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>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "The input crop implemented in the dw100 kernel driver is quite limited\nin that it doesn't allow arbitrary crop rectangles but only scale\nfactors quantized to the underlying fixed point representation and only\naspect ratio preserving crops.\n\nThe vertex map based implementation allows for pixel perfect crops. The\nonly downside is that ScalerCrop can no longer be set dynamically on\nolder kernels. Warn if the user tries to set ScalerCrop on such a setup.\n\nAs the vertex map is now set on start() it no longer needs to be updated\nfor frame==0.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\n---\n\nChanges in 0.9\n- Code cleanup\n- Update vertex map before start() to partially support old kernels\n\nChanges in 0.7:\n- Removed double call to applyVertexMap()\n---\n src/libcamera/pipeline/rkisp1/rkisp1.cpp | 118 +++++++++++------------\n 1 file changed, 59 insertions(+), 59 deletions(-)", "diff": "diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex 740791ac9c02..e8902ce66b70 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -235,9 +235,6 @@ private:\n \tRkISP1SelfPath selfPath_;\n \n \tstd::unique_ptr<ConverterDW100> dewarper_;\n-\tRectangle scalerMaxCrop_;\n-\n-\tstd::optional<Rectangle> activeCrop_;\n \n \t/* Internal buffers used when dewarper is being used */\n \tstd::vector<std::unique_ptr<FrameBuffer>> mainPathBuffers_;\n@@ -969,13 +966,22 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n \t\t\t\t\treturn ret;\n \n \t\t\t\t/*\n-\t\t\t\t * Calculate the crop rectangle of the data\n-\t\t\t\t * flowing into the dewarper in sensor\n-\t\t\t\t * coordinates.\n+\t\t\t\t * Apply the actual sensor crop, for proper\n+\t\t\t\t * dewarp map calculation\n+\t\t\t\t */\n+\t\t\t\tRectangle sensorCrop = outputCrop.transformedBetween(\n+\t\t\t\t\tinputCrop, sensorInfo.analogCrop);\n+\t\t\t\tauto &vertexMap = dewarper_->vertexMap(cfg.stream());\n+\t\t\t\tvertexMap.setSensorCrop(sensorCrop);\n+\t\t\t\tdata->properties_.set(properties::ScalerCropMaximum, sensorCrop);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Apply a default sensor crop that keeps the\n+\t\t\t\t * aspect ratio.\n \t\t\t\t */\n-\t\t\t\tscalerMaxCrop_ =\n-\t\t\t\t\toutputCrop.transformedBetween(inputCrop,\n-\t\t\t\t\t\t\t\t sensorInfo.analogCrop);\n+\t\t\t\tSize crop = format.size.boundedToAspectRatio(cfg.size);\n+\t\t\t\tvertexMap.setScalerCrop(crop.centeredTo(\n+\t\t\t\t\tRectangle(format.size).center()));\n \t\t\t}\n \n \t\t\tret = mainPath_.configure(ispCfg, format);\n@@ -1191,6 +1197,18 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL\n \t\tactions += [&]() { stat_->streamOff(); };\n \n \t\tif (data->usesDewarper_) {\n+\t\t\t/*\n+\t\t\t * Apply the vertex map before start to partially\n+\t\t\t * support ScalerCrop on kernels with a dw100 driver\n+\t\t\t * that has no dynamic vertex map/requests support.\n+\t\t\t */\n+\t\t\tif (controls && controls->contains(controls::ScalerCrop.id())) {\n+\t\t\t\tconst auto &crop = controls->get(controls::ScalerCrop);\n+\t\t\t\tauto &vertexMap = dewarper_->vertexMap(&data->mainPathStream_);\n+\t\t\t\tvertexMap.setScalerCrop(*crop);\n+\t\t\t}\n+\t\t\tdewarper_->applyVertexMap(&data->mainPathStream_);\n+\n \t\t\tret = dewarper_->start();\n \t\t\tif (ret) {\n \t\t\t\tLOG(RkISP1, Error) << \"Failed to start dewarper\";\n@@ -1345,25 +1363,31 @@ int PipelineHandlerRkISP1::updateControls(RkISP1CameraData *data)\n \n \tif (data->usesDewarper_) {\n \t\tstd::pair<Rectangle, Rectangle> cropLimits;\n-\t\tif (dewarper_->isConfigured(&data->mainPathStream_))\n-\t\t\tcropLimits = dewarper_->inputCropBounds(&data->mainPathStream_);\n-\t\telse\n+\t\tif (dewarper_->isConfigured(&data->mainPathStream_)) {\n+\t\t\tauto &vertexMap = dewarper_->vertexMap(&data->mainPathStream_);\n+\t\t\tvertexMap.applyLimits();\n+\t\t\tcropLimits = vertexMap.scalerCropBounds();\n+\t\t\tcontrols[&controls::ScalerCrop] = ControlInfo(cropLimits.first,\n+\t\t\t\t\t\t\t\t cropLimits.second,\n+\t\t\t\t\t\t\t\t vertexMap.effectiveScalerCrop());\n+\t\t} else {\n+\t\t\t/* This happens before configure() has run. Maybe we need a better solution.*/\n+\t\t\t/*\n+\t\t\t* ScalerCrop is specified to be in Sensor coordinates.\n+\t\t\t* So we need to transform the limits to sensor coordinates.\n+\t\t\t* We can safely assume that the maximum crop limit contains the\n+\t\t\t* full fov of the dewarper.\n+\t\t\t*/\n \t\t\tcropLimits = dewarper_->inputCropBounds();\n+\t\t\tRectangle maxCrop = Rectangle(data->sensor_->resolution());\n+\t\t\tRectangle min = cropLimits.first.transformedBetween(cropLimits.second,\n+\t\t\t\t\t\t\t\t\t maxCrop);\n \n-\t\t/*\n-\t\t * ScalerCrop is specified to be in Sensor coordinates.\n-\t\t * So we need to transform the limits to sensor coordinates.\n-\t\t * We can safely assume that the maximum crop limit contains the\n-\t\t * full fov of the dewarper.\n-\t\t */\n-\t\tRectangle min = cropLimits.first.transformedBetween(cropLimits.second,\n-\t\t\t\t\t\t\t\t scalerMaxCrop_);\n+\t\t\tcontrols[&controls::ScalerCrop] = ControlInfo(min,\n+\t\t\t\t\t\t\t\t maxCrop,\n+\t\t\t\t\t\t\t\t maxCrop);\n+\t\t}\n \n-\t\tcontrols[&controls::ScalerCrop] = ControlInfo(min,\n-\t\t\t\t\t\t\t scalerMaxCrop_,\n-\t\t\t\t\t\t\t scalerMaxCrop_);\n-\t\tdata->properties_.set(properties::ScalerCropMaximum, scalerMaxCrop_);\n-\t\tactiveCrop_ = scalerMaxCrop_;\n \n \t\tif (dewarper_->supportsRequests()) {\n \t\t\tcontrols[&controls::draft::Dw100Scale] = ControlInfo(0.2f, 8.0f, 1.0f);\n@@ -1415,8 +1439,6 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)\n \t/* Initialize the camera properties. */\n \tdata->properties_ = data->sensor_->properties();\n \n-\tscalerMaxCrop_ = Rectangle(data->sensor_->resolution());\n-\n \tconst CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();\n \tstd::unordered_map<uint32_t, DelayedControls::ControlParams> params = {\n \t\t{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },\n@@ -1675,42 +1697,20 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n \t\tupdate = true;\n \t}\n \n-\tif (update || info->frame == 0) {\n-\t\tdewarper_->applyVertexMap(&data->mainPathStream_, dewarpRequest);\n-\t}\n-\n \t/* Handle scaler crop control. */\n \tconst auto &crop = request->controls().get(controls::ScalerCrop);\n \tif (crop) {\n-\t\tRectangle rect = crop.value();\n-\n-\t\t/*\n-\t\t * ScalerCrop is specified to be in Sensor coordinates.\n-\t\t * So we need to transform it into dewarper coordinates.\n-\t\t * We can safely assume that the maximum crop limit contains the\n-\t\t * full fov of the dewarper.\n-\t\t */\n-\t\tstd::pair<Rectangle, Rectangle> cropLimits =\n-\t\t\tdewarper_->inputCropBounds(&data->mainPathStream_);\n-\n-\t\trect = rect.transformedBetween(scalerMaxCrop_, cropLimits.second);\n-\t\tint ret = dewarper_->setInputCrop(&data->mainPathStream_,\n-\t\t\t\t\t\t &rect);\n-\t\trect = rect.transformedBetween(cropLimits.second, scalerMaxCrop_);\n-\t\tif (!ret && rect != crop.value()) {\n-\t\t\t/*\n-\t\t\t * If the rectangle is changed by setInputCrop on the\n-\t\t\t * dewarper, log a debug message and cache the actual\n-\t\t\t * applied rectangle for metadata reporting.\n-\t\t\t */\n-\t\t\tLOG(RkISP1, Debug)\n-\t\t\t\t<< \"Applied rectangle \" << rect.toString()\n-\t\t\t\t<< \" differs from requested \" << crop.value().toString();\n-\t\t}\n-\n-\t\tactiveCrop_ = rect;\n+\t\tif (!dewarper_->supportsRequests())\n+\t\t\tLOG(RkISP1, Error)\n+\t\t\t\t<< \"Dynamically setting ScalerCrop requires a \"\n+\t\t\t\t \"dw100 driver with requests support\";\n+\t\tvertexMap.setScalerCrop(*crop);\n+\t\tupdate = true;\n \t}\n \n+\tif (update)\n+\t\tdewarper_->applyVertexMap(&data->mainPathStream_, dewarpRequest);\n+\n \t/*\n \t * Queue input and output buffers to the dewarper. The output\n \t * buffers for the dewarper are the buffers of the request, supplied\n@@ -1746,7 +1746,7 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n \tmeta.set(controls::draft::Dw100Scale, vertexMap.effectiveScale());\n \tmeta.set(controls::draft::Dw100Rotation, vertexMap.rotation());\n \tmeta.set(controls::draft::Dw100Offset, vertexMap.effectiveOffset());\n-\tmeta.set(controls::ScalerCrop, activeCrop_.value());\n+\tmeta.set(controls::ScalerCrop, vertexMap.effectiveScalerCrop());\n }\n \n void PipelineHandlerRkISP1::dewarpRequestReady(V4L2Request *request)\n", "prefixes": [ "v1", "24/33" ] }