From patchwork Mon Oct 26 17:19:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10260 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D7A56BDB13 for ; Mon, 26 Oct 2020 17:19:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 91ADB62046; Mon, 26 Oct 2020 18:19:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="YxhUgAbz"; dkim-atps=neutral Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 789E362035 for ; Mon, 26 Oct 2020 18:19:20 +0100 (CET) Received: by mail-wr1-x42d.google.com with SMTP id y12so13542365wrp.6 for ; Mon, 26 Oct 2020 10:19:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=JQzXIXipEcVEl2Rq6HsEkqgFc5bdahjLLiZ2swiWWEk=; b=YxhUgAbzsZBqLJftaJHoxUCN5LQJd7XgD7jgyal8SST55sorW9D5gfgWUvfuuriBiE wVFPsREkCQI8A8Vsr8dkGwlANg74uZAVqORTrD7vNiQ26EjlUqNsiO4JW5Uwbgj3UGal DDOU0DN+AkzdGFlkZAbaj9tSutXrxeZWAwyv+dfMRXlOwPVY3TDB26SZcpIAm2ILTSEy bJJB1QHQuO6QSOTo/MbVADxaWtDUUYqmIencWhoUEC5l5sPl79C1aj7QVsZAi7cYQgcY ENhETujpxYlhOedZPg7+4Meqjpzb1kS7hIvViOupooMvMwiGfIJJ6137YeL9rNSvYUKi lSiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JQzXIXipEcVEl2Rq6HsEkqgFc5bdahjLLiZ2swiWWEk=; b=Rkpf1zIVfllHmGPPVylEALTvDT1QyAolavC4STgOumondbwFzRIPYpqgk3mCYF7Ocs vDUhWuZGTAXSBJyeHcRd7eLVSh7xZ9EDiu2zQmpxhrfyOiKnScB3JRQcTPqRcrcEzaFY yy8mS7zKZLl5x7tC22bMNaUL1eEkWkwRNvzwAHyXrPFkyeWrlBx3ATV3OHee1KI41v0I EnCpk5AjHN2CuX9pF1Kb3Pr60XHLkw+nDu3BDY794yy8RQuEiYMiiR7qa9AK7ibAoeCw 9KoWNo3y4gpWGT+3Gs5AxcM+gt86787mcshx8YK0AtCzrazFiKxCRzJR0PeYUq3xWgp/ 4NBA== X-Gm-Message-State: AOAM530LoQv6Qnmz3Z2/sBRolhj7HidsxHMlAn7u+kdOY6vMoQNTpadK lmav2LXxVFKCC0TInmYtctvu3FaQJxAmMA== X-Google-Smtp-Source: ABdhPJy+hB6jdkJBI+3wkxUC/T2x7hMTDz61G2mKSMULAm+jf6dCx51pFw0yrbPsNhZ8dP3ptVNhig== X-Received: by 2002:a5d:4001:: with SMTP id n1mr19564163wrp.426.1603732759694; Mon, 26 Oct 2020 10:19:19 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id o63sm20955295wmo.2.2020.10.26.10.19.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Oct 2020 10:19:19 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 26 Oct 2020 17:19:07 +0000 Message-Id: <20201026171908.21463-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201026171908.21463-1-david.plowman@raspberrypi.com> References: <20201026171908.21463-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 5/6] libcamera: pipeline: raspberrypi: Implementation of digital zoom X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" During configure() we update the ScalerCropMaximum to the correct value for this camera mode and work out the minimum crop size allowed by the ISP. Whenever a new ScalerCrop request is received we check it's valid and apply it to the ISP V4L2 device. When the IPA returns its metadata to us we add the ScalerCrop information, rescaled to sensor native pixels. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/raspberrypi.cpp | 5 + .../pipeline/raspberrypi/raspberrypi.cpp | 92 +++++++++++++++---- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index b23baf2f..ff2faf86 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -62,6 +62,7 @@ static const ControlInfoMap Controls = { { &controls::Saturation, ControlInfo(0.0f, 32.0f) }, { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, + { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, }; } /* namespace RPi */ diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 1c255aa2..f338ff8b 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -699,6 +699,11 @@ void IPARPi::queueRequest(const ControlList &controls) break; } + case controls::SCALER_CROP: { + /* We do nothing with this, but should avoid the warning below. */ + break; + } + default: LOG(IPARPI, Warning) << "Ctrl " << controls::controls.at(ctrl.first)->name() diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 763451a8..b9d74a81 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -135,7 +135,7 @@ public: RPiCameraData(PipelineHandler *pipe) : CameraData(pipe), sensor_(nullptr), state_(State::Stopped), supportsFlips_(false), flipsAlterBayerOrder_(false), - dropFrameCount_(0), ispOutputCount_(0) + updateScalerCrop_(true), dropFrameCount_(0), ispOutputCount_(0) { } @@ -193,6 +193,13 @@ public: bool flipsAlterBayerOrder_; BayerFormat::Order nativeBayerOrder_; + /* For handling digital zoom. */ + CameraSensorInfo sensorInfo_; + Rectangle ispCrop_; /* crop in ISP (camera mode) pixels */ + Rectangle scalerCrop_; /* crop in sensor native pixels */ + bool updateScalerCrop_; + Size ispMinCropSize_; + unsigned int dropFrameCount_; private: @@ -677,26 +684,31 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) return ret; } - /* Adjust aspect ratio by providing crops on the input image. */ - Rectangle crop{ 0, 0, sensorFormat.size }; - - int ar = maxSize.height * sensorFormat.size.width - maxSize.width * sensorFormat.size.height; - if (ar > 0) - crop.width = maxSize.width * sensorFormat.size.height / maxSize.height; - else if (ar < 0) - crop.height = maxSize.height * sensorFormat.size.width / maxSize.width; + /* Figure out the smallest selection the ISP will allow. */ + Rectangle testCrop(0, 0, 1, 1); + data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop); + data->ispMinCropSize_ = testCrop.size(); - crop.width &= ~1; - crop.height &= ~1; + /* Adjust aspect ratio by providing crops on the input image. */ + Size size = sensorFormat.size.boundedToAspectRatio(maxSize); + Rectangle crop = size.centeredTo(Rectangle(sensorFormat.size).center()); + data->ispCrop_ = crop; - crop.x = (sensorFormat.size.width - crop.width) >> 1; - crop.y = (sensorFormat.size.height - crop.height) >> 1; data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); ret = data->configureIPA(config); if (ret) LOG(RPI, Error) << "Failed to configure the IPA: " << ret; + /* + * Update the ScalerCropMaximum to the correct value for this camera mode. + * For us, it's the same as the "analogue crop". + * + * \todo Make this property the ScalerCrop maximum value when dynamic + * controls are available and set it at validate() time + */ + data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop); + return ret; } @@ -1154,8 +1166,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) ipaConfig.data.push_back(static_cast(lsTable_.fd())); } - CameraSensorInfo sensorInfo = {}; - int ret = sensor_->sensorInfo(&sensorInfo); + /* We store the CameraSensorInfo for digital zoom calculations. */ + int ret = sensor_->sensorInfo(&sensorInfo_); if (ret) { LOG(RPI, Error) << "Failed to retrieve camera sensor info"; return ret; @@ -1164,7 +1176,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) /* Ready the IPA - it must know about the sensor resolution. */ IPAOperationData result; - ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, + ipa_->configure(sensorInfo_, streamConfig, entityControls, ipaConfig, &result); unsigned int resultIdx = 0; @@ -1243,8 +1255,26 @@ void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame, FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId); handleStreamBuffer(buffer, &isp_[Isp::Stats]); + /* Fill the Request metadata buffer with what the IPA has provided */ - requestQueue_.front()->metadata() = std::move(action.controls[0]); + Request *request = requestQueue_.front(); + request->metadata() = std::move(action.controls[0]); + + /* + * Also update the ScalerCrop in the metadata with what we actually + * used. But we must first rescale that from ISP (camera mode) pixels + * back into sensor native pixels. + * + * Sending this information on every frame may be helpful. + */ + if (updateScalerCrop_) { + updateScalerCrop_ = false; + scalerCrop_ = ispCrop_.scaledBy(sensorInfo_.analogCrop.size(), + sensorInfo_.outputSize); + scalerCrop_.translateBy(sensorInfo_.analogCrop.topLeft()); + } + request->metadata().set(controls::ScalerCrop, scalerCrop_); + state_ = State::IpaComplete; break; } @@ -1595,6 +1625,34 @@ void RPiCameraData::tryRunPipeline() /* Take the first request from the queue and action the IPA. */ Request *request = requestQueue_.front(); + if (request->controls().contains(controls::ScalerCrop)) { + Rectangle crop = request->controls().get(controls::ScalerCrop); + + if (crop.width && crop.height) { + /* First scale the crop from sensor native to camera mode pixels. */ + crop.translateBy(-sensorInfo_.analogCrop.topLeft()); + crop.scaleBy(sensorInfo_.outputSize, sensorInfo_.analogCrop.size()); + + /* + * The crop that we set must be: + * 1. At least as big as ispMinCropSize_, once that's been + * enlarged to the same aspect ratio. + * 2. With the same mid-point, if possible. + * 3. But it can't go outside the sensor area. + */ + Size minSize = ispMinCropSize_.expandedToAspectRatio(crop.size()); + Size size = crop.size().expandedTo(minSize); + crop = size.centeredTo(crop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize)); + + if (crop != ispCrop_) { + isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); + ispCrop_ = crop; + /* queueFrameAction will have to update its scalerCrop_ */ + updateScalerCrop_ = true; + } + } + } + /* * Process all the user controls by the IPA. Once this is complete, we * queue the ISP output buffer listed in the request to start the HW