[{"id":36726,"web_url":"https://patchwork.libcamera.org/comment/36726/","msgid":"<176242953955.2116251.12601473287438708741@neptunite.rasen.tech>","date":"2025-11-06T11:45:39","subject":"Re: [PATCH v2 23/35] libcamera: rkisp1: Use vertex map to implement\n\tScalerCrop","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Quoting Stefan Klug (2025-10-23 23:48:24)\n> The input crop implemented in the dw100 kernel driver is quite limited\n> in that it doesn't allow arbitrary crop rectangles but only scale\n> factors quantized to the underlying fixed point representation and only\n> aspect ratio preserving crops.\n> \n> The vertex map based implementation allows for pixel perfect crops. The\n> only downside is that ScalerCrop can no longer be set dynamically on\n> older kernels. Warn if the user tries to set ScalerCrop on such a setup.\n> \n> As the vertex map is now set on start() it no longer needs to be updated\n> for frame==0.\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> ---\n> \n> Changes in 0.9\n> - Code cleanup\n> - Update vertex map before start() to partially support old kernels\n> \n> Changes in 0.7:\n> - Removed double call to applyVertexMap()\n> ---\n>  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 115 ++++++++++++-----------\n>  1 file changed, 59 insertions(+), 56 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> index e22f05408931..0b31b8077c8d 100644\n> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> @@ -235,9 +235,6 @@ private:\n>         RkISP1SelfPath selfPath_;\n>  \n>         std::unique_ptr<ConverterDW100> dewarper_;\n> -       Rectangle scalerMaxCrop_;\n> -\n> -       std::optional<Rectangle> activeCrop_;\n>  \n>         /* Internal buffers used when dewarper is being used */\n>         std::vector<std::unique_ptr<FrameBuffer>> mainPathBuffers_;\n> @@ -969,13 +966,22 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n>                                         return ret;\n>  \n>                                 /*\n> -                                * Calculate the crop rectangle of the data\n> -                                * flowing into the dewarper in sensor\n> -                                * coordinates.\n> +                                * Apply the actual sensor crop, for proper\n> +                                * dewarp map calculation\n> +                                */\n> +                               Rectangle sensorCrop = outputCrop.transformedBetween(\n> +                                       inputCrop, sensorInfo.analogCrop);\n> +                               auto &vertexMap = dewarper_->vertexMap(cfg.stream());\n> +                               vertexMap.setSensorCrop(sensorCrop);\n> +                               data->properties_.set(properties::ScalerCropMaximum, sensorCrop);\n> +\n> +                               /*\n> +                                * Apply a default sensor crop that keeps the\n> +                                * aspect ratio.\n>                                  */\n> -                               scalerMaxCrop_ =\n> -                                       outputCrop.transformedBetween(inputCrop,\n> -                                                                     sensorInfo.analogCrop);\n> +                               Size crop = format.size.boundedToAspectRatio(cfg.size);\n> +                               vertexMap.setScalerCrop(crop.centeredTo(\n> +                                       Rectangle(format.size).center()));\n>                         }\n>  \n>                         ret = mainPath_.configure(ispCfg, format);\n> @@ -1186,6 +1192,18 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL\n>                 actions += [&]() { stat_->streamOff(); };\n>  \n>                 if (data->usesDewarper_) {\n> +                       /*\n> +                        * Apply the vertex map before start to partially\n> +                        * support ScalerCrop on kernels with a dw100 driver\n> +                        * that has no dynamic vertex map/requests support.\n> +                        */\n> +                       if (controls && controls->contains(controls::ScalerCrop.id())) {\n> +                               const auto &crop = controls->get(controls::ScalerCrop);\n> +                               auto &vertexMap = dewarper_->vertexMap(&data->mainPathStream_);\n> +                               vertexMap.setScalerCrop(*crop);\n> +                       }\n> +                       dewarper_->applyVertexMap(&data->mainPathStream_);\n> +\n>                         ret = dewarper_->start();\n>                         if (ret) {\n>                                 LOG(RkISP1, Error) << \"Failed to start dewarper\";\n> @@ -1338,25 +1356,31 @@ int PipelineHandlerRkISP1::updateControls(RkISP1CameraData *data)\n>  \n>         if (data->usesDewarper_) {\n>                 std::pair<Rectangle, Rectangle> cropLimits;\n> -               if (dewarper_->isConfigured(&data->mainPathStream_))\n> -                       cropLimits = dewarper_->inputCropBounds(&data->mainPathStream_);\n> -               else\n> +               if (dewarper_->isConfigured(&data->mainPathStream_)) {\n> +                       auto &vertexMap = dewarper_->vertexMap(&data->mainPathStream_);\n> +                       vertexMap.applyLimits();\n> +                       cropLimits = vertexMap.scalerCropBounds();\n> +                       controls[&controls::ScalerCrop] = ControlInfo(cropLimits.first,\n> +                                                                     cropLimits.second,\n> +                                                                     vertexMap.effectiveScalerCrop());\n> +               } else {\n> +                       /* This happens before configure() has run. Maybe we need a better solution.*/\n> +                       /*\n> +                       * ScalerCrop is specified to be in Sensor coordinates.\n> +                       * So we need to transform the limits to sensor coordinates.\n> +                       * We can safely assume that the maximum crop limit contains the\n> +                       * full fov of the dewarper.\n> +                       */\n>                         cropLimits = dewarper_->inputCropBounds();\n> +                       Rectangle maxCrop = Rectangle(data->sensor_->resolution());\n> +                       Rectangle min = cropLimits.first.transformedBetween(cropLimits.second,\n> +                                                                           maxCrop);\n> +\n> +                       controls[&controls::ScalerCrop] = ControlInfo(min,\n> +                                                                     maxCrop,\n> +                                                                     maxCrop);\n> +               }\n>  \n> -               /*\n> -                * ScalerCrop is specified to be in Sensor coordinates.\n> -                * So we need to transform the limits to sensor coordinates.\n> -                * We can safely assume that the maximum crop limit contains the\n> -                * full fov of the dewarper.\n> -                */\n> -               Rectangle min = cropLimits.first.transformedBetween(cropLimits.second,\n> -                                                                   scalerMaxCrop_);\n> -\n> -               controls[&controls::ScalerCrop] = ControlInfo(min,\n> -                                                             scalerMaxCrop_,\n> -                                                             scalerMaxCrop_);\n> -               data->properties_.set(properties::ScalerCropMaximum, scalerMaxCrop_);\n> -               activeCrop_ = scalerMaxCrop_;\n>         }\n>  \n>         /* Add the IPA registered controls to list of camera controls. */\n> @@ -1397,8 +1421,6 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)\n>         /* Initialize the camera properties. */\n>         data->properties_ = data->sensor_->properties();\n>  \n> -       scalerMaxCrop_ = Rectangle(data->sensor_->resolution());\n> -\n>         const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();\n>         std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {\n>                 { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },\n> @@ -1632,36 +1654,17 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n>                 availableDewarpRequests_.pop();\n>         }\n>  \n> +       auto &vertexMap = dewarper_->vertexMap(&data->mainPathStream_);\n> +\n>         /* Handle scaler crop control. */\n>         const auto &crop = request->controls().get(controls::ScalerCrop);\n>         if (crop) {\n> -               Rectangle rect = crop.value();\n> -\n> -               /*\n> -                * ScalerCrop is specified to be in Sensor coordinates.\n> -                * So we need to transform it into dewarper coordinates.\n> -                * We can safely assume that the maximum crop limit contains the\n> -                * full fov of the dewarper.\n> -                */\n> -               std::pair<Rectangle, Rectangle> cropLimits =\n> -                       dewarper_->inputCropBounds(&data->mainPathStream_);\n> -\n> -               rect = rect.transformedBetween(scalerMaxCrop_, cropLimits.second);\n> -               int ret = dewarper_->setInputCrop(&data->mainPathStream_,\n> -                                                 &rect);\n> -               rect = rect.transformedBetween(cropLimits.second, scalerMaxCrop_);\n> -               if (!ret && rect != crop.value()) {\n> -                       /*\n> -                        * If the rectangle is changed by setInputCrop on the\n> -                        * dewarper, log a debug message and cache the actual\n> -                        * applied rectangle for metadata reporting.\n> -                        */\n> -                       LOG(RkISP1, Debug)\n> -                               << \"Applied rectangle \" << rect.toString()\n> -                               << \" differs from requested \" << crop.value().toString();\n> -               }\n> -\n> -               activeCrop_ = rect;\n> +               if (!dewarper_->supportsRequests())\n> +                       LOG(RkISP1, Error)\n\nDid you intend for this to be Error? The commit message says it should be Warning.\n\nEither way, looks good to me.\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> +                               << \"Dynamically setting ScalerCrop requires a \"\n> +                                  \"dw100 driver with requests support\";\n> +               vertexMap.setScalerCrop(*crop);\n> +               dewarper_->applyVertexMap(&data->mainPathStream_, dewarpRequest);\n>         }\n>  \n>         /*\n> @@ -1695,7 +1698,7 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n>                 }\n>         }\n>  \n> -       request->metadata().set(controls::ScalerCrop, activeCrop_.value());\n> +       request->metadata().set(controls::ScalerCrop, vertexMap.effectiveScalerCrop());\n>  }\n>  \n>  void PipelineHandlerRkISP1::dewarpRequestReady(V4L2Request *request)\n> -- \n> 2.48.1\n>","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 0E06FC3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  6 Nov 2025 11:45:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A62BE60A85;\n\tThu,  6 Nov 2025 12:45:46 +0100 (CET)","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 AED79606E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  6 Nov 2025 12:45:45 +0100 (CET)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:d4d0:27ea:7a74:8a9e])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 44E806A6;\n\tThu,  6 Nov 2025 12:43:50 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"fV9H1vOX\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1762429430;\n\tbh=6SHp7Mc2Xdkt/PMhTmWW1x/xVNIGcOBYI3KBizcPfTs=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=fV9H1vOX+/M2k8K/qvpZIyu9c4ZVp7SufM0B/KkjLU7FQJppupTs6RVfew7f5BZlV\n\tiKBDCOp5EkxnV5m9ORq/EYMD9Oh9khCB5nhJE3RP0PlQz5Y2H/tmhTbpxiZ/O88AuU\n\tHb+XTIGsSz9qnL04t3xWT1F/NVI7+FMGwcbPNhE0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251023144841.403689-24-stefan.klug@ideasonboard.com>","References":"<20251023144841.403689-1-stefan.klug@ideasonboard.com>\n\t<20251023144841.403689-24-stefan.klug@ideasonboard.com>","Subject":"Re: [PATCH v2 23/35] libcamera: rkisp1: Use vertex map to implement\n\tScalerCrop","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 06 Nov 2025 20:45:39 +0900","Message-ID":"<176242953955.2116251.12601473287438708741@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","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>"}}]