From patchwork Wed Jul 29 09:31:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9060 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 29FE8BD86F for ; Wed, 29 Jul 2020 09:32:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0F3E36185C; Wed, 29 Jul 2020 11:32:04 +0200 (CEST) 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="cNyMNGL1"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 220D7605B2 for ; Wed, 29 Jul 2020 11:32:02 +0200 (CEST) Received: by mail-wm1-x32a.google.com with SMTP id k8so2249328wma.2 for ; Wed, 29 Jul 2020 02:32:02 -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=xonQ42icnoeM5+ZjLptP5FX3WqLgo0G6u2lfNy8TDyY=; b=cNyMNGL1xHiJJEqO5zgRqrA2N+QFkyduLP9e9BL0OxDgutrXmaotcRXAnifN3ET962 nnouRU1jBgGgCf2nwEf71qUkI+5avCVrE+2J2faqItdd9aWppo18F9+RPvRgUogMfamn s6WlCTVq8GjZqdbyVRMEuCGFgiq8UhVe86mHiR15Bct2KglEN673kXAv6QU8qECpa1Go u1Uu+GwQT/Hi7CqcNFeIr0m2D6YFDrLGbL02mznGuOWnDYMk5sDwbSLf7Fdxe0889a/O SGHH+8S17YO7qsrqowBLEj0lQ1T59a4iE9Cf4OfhUoAR3gAVPXEAHCp95arxBj92aFhT Wr0w== 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=xonQ42icnoeM5+ZjLptP5FX3WqLgo0G6u2lfNy8TDyY=; b=Ji0CLc6bEstNdAv0pyU24IEjYLy3lDXDbSa+i7OUxXKXtVMCly0ayD1rNN7v30WgJj bVXfjmWzWdgTGFDWXt4xXVtONT/pJ7mAFHs/C/Gj2nUlbo7UxNCCcq7TUP8girZF+8m/ hWLXSN3Gnuv1MrcGpSxQhHhrudJVZXzsKxYtWLVNU4c0MwzTnHJz4uK0iuGfA8thKAvo 56bOGs4a4tmxxpj4btlHYiyUastLjS583W0pdpeAlXgcJH9NUYNzHgSydHbPkRHUVZi1 65LEfAdYuJGlNpQM51UcZY2Qc864C8kAEgb/bjY/XuKzIAjIdDvKyW8CpRIVajOuQIMo +iPw== X-Gm-Message-State: AOAM5303D+z99ZbUAIXQGzHRSidS1rBiGoRPTLcuLVUyiiQhoJVvUjxa NEKFDcvmbGaiEi6llgczy64lJJAVh/2PiA== X-Google-Smtp-Source: ABdhPJyr9oVy0aclePILz269u8DZCDXDXkh+LmEofO+OW9D/Q3OX0MaEAA6bu3HtrOx19jS9t03wcQ== X-Received: by 2002:a1c:3b06:: with SMTP id i6mr7903903wma.42.1596015121151; Wed, 29 Jul 2020 02:32:01 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id c15sm3709921wme.23.2020.07.29.02.32.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2020 02:32:00 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 29 Jul 2020 10:31:50 +0100 Message-Id: <20200729093154.12296-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200729093154.12296-1-david.plowman@raspberrypi.com> References: <20200729093154.12296-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/5] libcamera: Add Transform class 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" Add implementation of 2d plane transforms, and include a Transform field in the CameraConfiguration to record the application's choice of image transform for this session. --- include/libcamera/camera.h | 3 + include/libcamera/meson.build | 1 + include/libcamera/transform.h | 193 ++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 include/libcamera/transform.h diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 4d1a4a9..fd9355e 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace libcamera { @@ -60,6 +61,8 @@ public: bool empty() const; std::size_t size() const; + Transform userTransform; + protected: CameraConfiguration(); diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index cdb8e03..7fae5e5 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -19,6 +19,7 @@ libcamera_public_headers = files([ 'span.h', 'stream.h', 'timer.h', + 'transform.h', ]) include_dir = join_paths(libcamera_include_dir, 'libcamera') diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h new file mode 100644 index 0000000..cfd5026 --- /dev/null +++ b/include/libcamera/transform.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * transform.h - Implementation of 2d plane transforms + */ + +#ifndef __LIBCAMERA_TRANSFORM_H__ +#define __LIBCAMERA_TRANSFORM_H__ + +#include + +namespace libcamera { + +class Transform +{ +public: + constexpr Transform() + : Transform(Identity) + { + } + + constexpr static Transform identity() + { + return Transform(Identity); + } + + constexpr static Transform rot0() + { + return identity(); + } + + constexpr static Transform hflip() + { + return Transform(HFlip); + } + + constexpr static Transform vflip() + { + return Transform(VFlip); + } + + constexpr static Transform hvflip() + { + return Transform(HVFlip); + } + + constexpr static Transform rot180() + { + return hvflip(); + } + + constexpr static Transform transpose() + { + return Transform(Transpose); + } + + constexpr static Transform rot270() + { + return Transform(Rot270); + } + + constexpr static Transform rot90() + { + return Transform(Rot90); + } + + constexpr static Transform rot180Transpose() + { + return Transform(Rot180Transpose); + } + + constexpr static Transform hvflipTranspose() + { + return rot180Transpose(); + } + + constexpr static bool rotation(int32_t angle, Transform &xfm) + { + bool success = true; + angle = angle % 360; + if (angle < 0) + angle += 360; + + if (angle == 0) + xfm = identity(); + else if (angle == 90) + xfm = rot90(); + else if (angle == 180) + xfm = rot180(); + else if (angle == 270) + xfm = rot270(); + else + success = false; + + return success; + } + + constexpr bool rotation(int32_t &angle) const + { + bool success = true; + + if (type_ == Identity) + angle = 0; + else if (type_ == Rot90) + angle = 90; + else if (type_ == HVFlip) + angle = 180; + else if (type_ == Rot270) + angle = 270; + else + success = false; + + return success; + } + + constexpr bool contains(Transform xfm) const + { + return (type_ & xfm.type_) == xfm.type_; + } + + constexpr Transform inverse() const + { + /* They're all self-inverses, except for Rot90 and Rot270. */ + const Type inverses[] = { Identity, HFlip, VFlip, HVFlip, + Transpose, Rot90, Rot270, Rot180Transpose }; + + return Transform(inverses[type_]); + } + + constexpr Transform operator*(Transform xfm) const + { + /* + * Reorder the operations so that we imagine doing xfm's transpose + * (if any) after our flips. The effect is to swap our hflips for + * vflips and vice versa, after which we can just xor all the bits. + */ + int reorderedType = type_; + if (xfm.type_ & Transpose) + reorderedType = (type_ & 4) | ((type_ & 1) << 1) | ((type_ & 2) >> 1); + + return Transform((Type)(reorderedType ^ xfm.type_)); + } + + bool operator==(Transform xfm) const + { + return type_ == xfm.type_; + } + + bool operator!=(Transform xfm) const + { + return !(*this == xfm); + } + + std::string toString() const + { + char const *strings[] = { + "identity", + "hflip", + "vflip", + "hvflip", + "transpose", + "rot270", + "rot90", + "rot180transpose" + }; + return strings[type_]; + } + +private: + enum Type : int { + Identity = 0, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose + }; + + constexpr Transform(Type type) + : type_(type) + { + } + + Type type_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_TRANSFORM_H__ */ + From patchwork Wed Jul 29 09:31:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9061 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 920F4BD86F for ; Wed, 29 Jul 2020 09:32:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D9FCD616FF; Wed, 29 Jul 2020 11:32:04 +0200 (CEST) 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="E5/rv9y1"; dkim-atps=neutral Received: from mail-wm1-x341.google.com (mail-wm1-x341.google.com [IPv6:2a00:1450:4864:20::341]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AB787605B2 for ; Wed, 29 Jul 2020 11:32:02 +0200 (CEST) Received: by mail-wm1-x341.google.com with SMTP id 184so2262403wmb.0 for ; Wed, 29 Jul 2020 02:32:02 -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=HKmAuBxwIumr230KzlwI2CVDGfwWcBPc8QjmYgrFHx0=; b=E5/rv9y15EmQedP9cGMPqnZkyVkpaBOpS2W3M1mUPRN/5DjelgK0SCEXVeQn5ldOfI a1v2er30GXQrQRIG6kJ3gx8DOx4IMr1DdYxUp90C8LJ3cg71GMhIR8ijddbAzdWjLvPj eOpZdXDO9q129ZBPrDakuz3oV5MuXPSyJMdMg1W1IfCtJj7fRhIDjQILulg1JMm0ZXRH SJ5uOAxDpvdAZAg/3qKkAqS0LzB69K3UK5vjWG6dUgEigURXKgeEIjloHzGMEFS0aM/i aEXr3ZRMg1BrLwTooEfQ/m8r9hs2L+nL919NO+w0ykcE31IUzNyvsXWeXjExrwiBe5BH fA1Q== 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=HKmAuBxwIumr230KzlwI2CVDGfwWcBPc8QjmYgrFHx0=; b=cZK6iXy9QyTxhwMRM8fBbkniaTPVxv6n/POL/prYCMaoqjsfEm/W+EQ9iHgx86LXdX ok9JcZbLjEpESjgDbynBNHVf+l+q13BWt/Nb0C4PT3zj6+D4pYLklC5liSjymkC7YFiI n53BgJYHmBItfrNT0DHNyVQnwA6Etz5YowBpYVKUjcFSfLki0XDpn7BM7cSNgB5yocuV +NzL4rAeP30n/fKcmttk2uS165dfXBOd4FA1U7uOzWpr/2nubqIaoYTlvj7KsV7CmgLQ 1JTeMGcrB4e/ijQCVtBvt/Rqjo4AI0zqk+h3tyLvG6OaxZLfN02KvHB/N6z/tLyc0Onv Xrpw== X-Gm-Message-State: AOAM533jebKUmaHGbS0N01tlGPL5Oc88m1rwMg6nkH1lX8+F4PD+5ffY jiEHdcuSvC/73xai40BgiICCPbMGPqGwYw== X-Google-Smtp-Source: ABdhPJx3lc34VHsZNJ8gokXd7ru+x6odi7uLWe6vmQ6gUUTcrwO2tmHb/4eT0MOddQTDN+CZzt/xdg== X-Received: by 2002:a7b:c054:: with SMTP id u20mr7665782wmc.2.1596015121835; Wed, 29 Jul 2020 02:32:01 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id c15sm3709921wme.23.2020.07.29.02.32.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2020 02:32:01 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 29 Jul 2020 10:31:51 +0100 Message-Id: <20200729093154.12296-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200729093154.12296-1-david.plowman@raspberrypi.com> References: <20200729093154.12296-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/5] libcamera: raspberrypi: Apply transform and pass through to IPA 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" The user-supplied transform is applied to the camera, taking account of any pre-existing rotation. It is then also plumbed through to the IPA so that it will (in a further commit) be able to make use of it. --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/raspberrypi.cpp | 13 ++++++++- .../pipeline/raspberrypi/raspberrypi.cpp | 27 +++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index ca62990..3905a4a 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -14,6 +14,7 @@ enum RPiConfigParameters { RPI_IPA_CONFIG_LS_TABLE = (1 << 0), RPI_IPA_CONFIG_STAGGERED_WRITE = (1 << 1), RPI_IPA_CONFIG_SENSOR = (1 << 2), + RPI_IPA_CONFIG_TRANSFORM = (1 << 3), }; enum RPiOperations { diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 3747208..2809521 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -144,6 +145,9 @@ private: /* LS table allocation passed in from the pipeline handler. */ FileDescriptor lsTableHandle_; void *lsTable_; + + /* This is the transform requested by the user/application driving libcamera. */ + Transform userTransform_; }; int IPARPi::init(const IPASettings &settings) @@ -282,6 +286,7 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, lastMode_ = mode_; /* Store the lens shading table pointer and handle if available. */ + unsigned int next_element = 0; if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { /* Remove any previous table, if there was one. */ if (lsTable_) { @@ -290,7 +295,7 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, } /* Map the LS table buffer into user space. */ - lsTableHandle_ = FileDescriptor(ipaConfig.data[0]); + lsTableHandle_ = FileDescriptor(ipaConfig.data[next_element++]); if (lsTableHandle_.isValid()) { lsTable_ = mmap(nullptr, MAX_LS_GRID_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, lsTableHandle_.fd(), 0); @@ -301,6 +306,12 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, } } } + + /* Fish out any transform set by the user/application. */ + if (ipaConfig.operation & RPI_IPA_CONFIG_TRANSFORM) { + uint32_t transformType = ipaConfig.data[next_element++]; + userTransform_ = reinterpret_cast(transformType); + } } void IPARPi::mapBuffers(const std::vector &buffers) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 82a0a4d..5fb427a 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -324,6 +324,9 @@ public: uint32_t expectedSequence_; bool sensorMetadata_; + /* This is the transform requested by the user/application driving libcamera. */ + Transform userTransform_; + /* * All the functions in this class are called from a single calling * thread. So, we do not need to have any mutex to protect access to any @@ -400,6 +403,9 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (userTransform.contains(Transform::transpose())) + return Invalid; + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair outSize[2]; Size maxSize; @@ -609,6 +615,9 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) unsigned int maxIndex = 0; bool rawStream = false; + /* Record the transform requested by the application. */ + data->userTransform_ = config->userTransform; + /* * Look for the RAW stream (if given) size as well as the largest * ISP output size. @@ -1140,6 +1149,11 @@ int RPiCameraData::configureIPA() ipaConfig.data = { static_cast(lsTable_.fd()) }; } + /* We must pass the user transform to the IPA too. */ + uint32_t transformType = reinterpret_cast(userTransform_); + ipaConfig.operation |= RPI_IPA_CONFIG_TRANSFORM; + ipaConfig.data.push_back(transformType); + CameraSensorInfo sensorInfo = {}; int ret = sensor_->sensorInfo(&sensorInfo); if (ret) { @@ -1168,8 +1182,17 @@ int RPiCameraData::configureIPA() /* Configure the H/V flip controls based on the sensor rotation. */ ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); int32_t rotation = sensor_->properties().get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); + + /* The camera needs to compose the user transform with the rotation. */ + Transform rotationTransform; + if (Transform::rotation(rotation, rotationTransform) == false) + return -EINVAL; + Transform transform = userTransform_ * rotationTransform; + + ctrls.set(V4L2_CID_HFLIP, + static_cast(transform.contains(Transform::hflip()))); + ctrls.set(V4L2_CID_VFLIP, + static_cast(transform.contains(Transform::vflip()))); unicam_[Unicam::Image].dev()->setControls(&ctrls); } From patchwork Wed Jul 29 09:31:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9064 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 F02D4BD86F for ; Wed, 29 Jul 2020 09:32:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90ACB61A22; Wed, 29 Jul 2020 11:32:08 +0200 (CEST) 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="tjNLQ59y"; dkim-atps=neutral Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3F46E618F9 for ; Wed, 29 Jul 2020 11:32:04 +0200 (CEST) Received: by mail-wm1-x32e.google.com with SMTP id k8so2249446wma.2 for ; Wed, 29 Jul 2020 02:32:04 -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=lgGaAYi8JaqVKMBv1HpSYJu1triMv0BwSG5MsK1P0eY=; b=tjNLQ59yyx+XWiY+fkpkSFbW9A1Wj4OsY6o97Kp+i2ZDQe1W45Maes/b+CjZwKBj6v Cp0qpA56qKL6gaAc0RJ5O67TleKu+RIPnZM9/GA8x+gYCqugNZIerrABW7jAKUX+Thv9 k5gz1w9G+lum0qE+F7E1GWJwi3OIEkX39bF/+emS1y5HfnNlixlSbWGZqg/ZezbTC27x Q3HwB66NPplwKWHdhhOtZFxF52WWj22mXu1uZ8BPlv/URge+RyG6UN9rsfuyvCL8VlKF 9ISoi2fZcqHvS4zWvouifCk5JqNOEY8h6U5JRdIvh+2lMGP02ulHXM6sH+JEltr8bngu cnHg== 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=lgGaAYi8JaqVKMBv1HpSYJu1triMv0BwSG5MsK1P0eY=; b=loHG0+zsTm/hALIc4+oIZ4F/66Ft4NTXU+AV5g52XfFJlX0rdHopsBtrvZENyf+e/J cMMxOdprVSU7k0W3S6IXkYmWJ/8M1t1KVii3gwWNrtatneeUqbnJ4zj0aGT7OqLcf2bc cFV/LNYCWkE6+aJ4ZwiAHT6FZ3CfdxfFPIcuDyDIa4Yg2Hyw1RbLCNYslRr4wKDKSkcj ylxALHTY/WYOfKt2JBCmoTmd15jMGQTfUnN1UpTvh9YjKCcl8E1zT0U9Ks3sYHaOdktu 311tladfzXLbuedSDHrDlAu9s1bZRW/K4eLHPn17PSeqbih3+2COX9oy5e0Y4fzsU86+ QAnQ== X-Gm-Message-State: AOAM533MV/QE9pn5dC0688xbxk+yDlJDisvcC9mwAP9/b47t2KmlpWN/ 50l93nZY0LQAdx4eOn/ahBCONndtTorQYQ== X-Google-Smtp-Source: ABdhPJxrW6FUGGhKzpZF3KTFV4rFy8oDgsqQhUVIv6lk3jFKgm8+hlfmyBnHWu2gIGVQ/Ic/E70iqw== X-Received: by 2002:a1c:7705:: with SMTP id t5mr8117452wmi.112.1596015122588; Wed, 29 Jul 2020 02:32:02 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id c15sm3709921wme.23.2020.07.29.02.32.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2020 02:32:02 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 29 Jul 2020 10:31:52 +0100 Message-Id: <20200729093154.12296-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200729093154.12296-1-david.plowman@raspberrypi.com> References: <20200729093154.12296-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/5] libcamera: raspberrypi: Plumb user transform through to individual IPAs 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" This commit takes the user/application supplied Transform and plumbs it through to all our individual IPAs using the SwitchMode method of the Controller/Algorithms. IPARPi::configure has to be re-ordered just a little to ensure the userTransform_ is available before calling SwitchMode. --- src/ipa/raspberrypi/controller/algorithm.cpp | 4 ++- src/ipa/raspberrypi/controller/algorithm.hpp | 3 +- src/ipa/raspberrypi/controller/controller.cpp | 5 +-- src/ipa/raspberrypi/controller/controller.hpp | 5 ++- src/ipa/raspberrypi/controller/rpi/agc.cpp | 3 +- src/ipa/raspberrypi/controller/rpi/agc.hpp | 3 +- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 3 +- src/ipa/raspberrypi/controller/rpi/alsc.hpp | 3 +- src/ipa/raspberrypi/controller/rpi/noise.cpp | 4 ++- src/ipa/raspberrypi/controller/rpi/noise.hpp | 3 +- .../raspberrypi/controller/rpi/sharpen.cpp | 3 +- .../raspberrypi/controller/rpi/sharpen.hpp | 3 +- src/ipa/raspberrypi/raspberrypi.cpp | 31 ++++++++++--------- 13 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/ipa/raspberrypi/controller/algorithm.cpp b/src/ipa/raspberrypi/controller/algorithm.cpp index 55cb201..b95ebb8 100644 --- a/src/ipa/raspberrypi/controller/algorithm.cpp +++ b/src/ipa/raspberrypi/controller/algorithm.cpp @@ -16,9 +16,11 @@ void Algorithm::Read(boost::property_tree::ptree const ¶ms) void Algorithm::Initialise() {} -void Algorithm::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Algorithm::SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) { (void)camera_mode; + (void)transform; (void)metadata; } diff --git a/src/ipa/raspberrypi/controller/algorithm.hpp b/src/ipa/raspberrypi/controller/algorithm.hpp index 187c50c..b38f167 100644 --- a/src/ipa/raspberrypi/controller/algorithm.hpp +++ b/src/ipa/raspberrypi/controller/algorithm.hpp @@ -37,7 +37,8 @@ public: virtual void Resume() { paused_ = false; } virtual void Read(boost::property_tree::ptree const ¶ms); virtual void Initialise(); - virtual void SwitchMode(CameraMode const &camera_mode, Metadata *metadata); + virtual void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata); virtual void Prepare(Metadata *image_metadata); virtual void Process(StatisticsPtr &stats, Metadata *image_metadata); Metadata &GetGlobalMetadata() const diff --git a/src/ipa/raspberrypi/controller/controller.cpp b/src/ipa/raspberrypi/controller/controller.cpp index 7c4b04f..7a4310f 100644 --- a/src/ipa/raspberrypi/controller/controller.cpp +++ b/src/ipa/raspberrypi/controller/controller.cpp @@ -56,11 +56,12 @@ void Controller::Initialise() RPI_LOG("Controller finished"); } -void Controller::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Controller::SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) { RPI_LOG("Controller starting"); for (auto &algo : algorithms_) - algo->SwitchMode(camera_mode, metadata); + algo->SwitchMode(camera_mode, transform, metadata); switch_mode_called_ = true; RPI_LOG("Controller finished"); } diff --git a/src/ipa/raspberrypi/controller/controller.hpp b/src/ipa/raspberrypi/controller/controller.hpp index 6ba9412..738c4b6 100644 --- a/src/ipa/raspberrypi/controller/controller.hpp +++ b/src/ipa/raspberrypi/controller/controller.hpp @@ -15,6 +15,8 @@ #include +#include + #include "camera_mode.h" #include "device_status.h" #include "metadata.hpp" @@ -39,7 +41,8 @@ public: Algorithm *CreateAlgorithm(char const *name); void Read(char const *filename); void Initialise(); - void SwitchMode(CameraMode const &camera_mode, Metadata *metadata); + void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata); void Prepare(Metadata *image_metadata); void Process(StatisticsPtr stats, Metadata *image_metadata); Metadata &GetGlobalMetadata(); diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp index c02b5ec..ceac771 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -221,7 +221,8 @@ void Agc::SetConstraintMode(std::string const &constraint_mode_name) constraint_mode_name_ = constraint_mode_name; } -void Agc::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Agc::SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) { // On a mode switch, it's possible the exposure profile could change, // so we run through the dividing up of exposure/gain again and diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp index 9a7e89c..78f016e 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp @@ -75,7 +75,8 @@ public: void SetMeteringMode(std::string const &metering_mode_name) override; void SetExposureMode(std::string const &exposure_mode_name) override; void SetConstraintMode(std::string const &contraint_mode_name) override; - void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override; + void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) override; void Prepare(Metadata *image_metadata) override; void Process(StatisticsPtr &stats, Metadata *image_metadata) override; diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp index 76e2f04..5fac9dd 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp @@ -12,6 +12,7 @@ // Raspberry Pi ALSC (Auto Lens Shading Correction) algorithm. using namespace RPi; +using libcamera::Transform; #define NAME "rpi.alsc" @@ -173,7 +174,7 @@ void Alsc::Initialise() lambda_r_[i] = lambda_b_[i] = 1.0; } -void Alsc::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Alsc::SwitchMode(CameraMode const &camera_mode, Transform transform, Metadata *metadata) { (void)metadata; diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.hpp b/src/ipa/raspberrypi/controller/rpi/alsc.hpp index 3806257..9b54578 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.hpp @@ -50,7 +50,8 @@ public: ~Alsc(); char const *Name() const override; void Initialise() override; - void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override; + void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) override; void Read(boost::property_tree::ptree const ¶ms) override; void Prepare(Metadata *image_metadata) override; void Process(StatisticsPtr &stats, Metadata *image_metadata) override; diff --git a/src/ipa/raspberrypi/controller/rpi/noise.cpp b/src/ipa/raspberrypi/controller/rpi/noise.cpp index 2cafde3..0f4002b 100644 --- a/src/ipa/raspberrypi/controller/rpi/noise.cpp +++ b/src/ipa/raspberrypi/controller/rpi/noise.cpp @@ -27,8 +27,10 @@ char const *Noise::Name() const return NAME; } -void Noise::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Noise::SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) { + (void)transform; (void)metadata; // For example, we would expect a 2x2 binned mode to have a "noise diff --git a/src/ipa/raspberrypi/controller/rpi/noise.hpp b/src/ipa/raspberrypi/controller/rpi/noise.hpp index 25bf188..37a22fd 100644 --- a/src/ipa/raspberrypi/controller/rpi/noise.hpp +++ b/src/ipa/raspberrypi/controller/rpi/noise.hpp @@ -18,7 +18,8 @@ class Noise : public Algorithm public: Noise(Controller *controller); char const *Name() const override; - void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override; + void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) override; void Read(boost::property_tree::ptree const ¶ms) override; void Prepare(Metadata *image_metadata) override; diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp b/src/ipa/raspberrypi/controller/rpi/sharpen.cpp index 2b701db..64d269d 100644 --- a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp +++ b/src/ipa/raspberrypi/controller/rpi/sharpen.cpp @@ -26,7 +26,8 @@ char const *Sharpen::Name() const return NAME; } -void Sharpen::SwitchMode(CameraMode const &camera_mode, Metadata *metadata) +void Sharpen::SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) { (void)metadata; diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.hpp b/src/ipa/raspberrypi/controller/rpi/sharpen.hpp index 568521b..8a1c164 100644 --- a/src/ipa/raspberrypi/controller/rpi/sharpen.hpp +++ b/src/ipa/raspberrypi/controller/rpi/sharpen.hpp @@ -18,7 +18,8 @@ class Sharpen : public SharpenAlgorithm public: Sharpen(Controller *controller); char const *Name() const override; - void SwitchMode(CameraMode const &camera_mode, Metadata *metadata) override; + void SwitchMode(CameraMode const &camera_mode, libcamera::Transform transform, + Metadata *metadata) override; void Read(boost::property_tree::ptree const ¶ms) override; void SetStrength(double strength) override; void Prepare(Metadata *image_metadata) override; diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 2809521..a8676fb 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -270,21 +270,6 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, agcStatus.analogue_gain = DEFAULT_ANALOGUE_GAIN; } - RPi::Metadata metadata; - controller_.SwitchMode(mode_, &metadata); - - /* SwitchMode may supply updated exposure/gain values to use. */ - metadata.Get("agc.status", agcStatus); - if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) { - ControlList ctrls(unicam_ctrls_); - applyAGC(&agcStatus, ctrls); - result->controls.push_back(ctrls); - - result->operation |= RPI_IPA_CONFIG_SENSOR; - } - - lastMode_ = mode_; - /* Store the lens shading table pointer and handle if available. */ unsigned int next_element = 0; if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { @@ -312,6 +297,22 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, uint32_t transformType = ipaConfig.data[next_element++]; userTransform_ = reinterpret_cast(transformType); } + + /* Finally we can tell the IPAs about the new camera mode. */ + RPi::Metadata metadata; + controller_.SwitchMode(mode_, userTransform_, &metadata); + + /* SwitchMode may supply updated exposure/gain values to use. */ + metadata.Get("agc.status", agcStatus); + if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) { + ControlList ctrls(unicam_ctrls_); + applyAGC(&agcStatus, ctrls); + result->controls.push_back(ctrls); + + result->operation |= RPI_IPA_CONFIG_SENSOR; + } + + lastMode_ = mode_; } void IPARPi::mapBuffers(const std::vector &buffers) From patchwork Wed Jul 29 09:31:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9062 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 C8CD8BD86F for ; Wed, 29 Jul 2020 09:32:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 94D996185C; Wed, 29 Jul 2020 11:32:07 +0200 (CEST) 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="qHOsoKT6"; dkim-atps=neutral Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DBCDE611A2 for ; Wed, 29 Jul 2020 11:32:03 +0200 (CEST) Received: by mail-wr1-x443.google.com with SMTP id b6so20937787wrs.11 for ; Wed, 29 Jul 2020 02:32:03 -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=5PMt33dqXzIUjSsz8EwXzOq9fkust7oXx3uBnBL8KqI=; b=qHOsoKT6HwVvQug4GrEMbiRqSxZhD35c5xxTq7oy6Iiz1Jf9S7q+KRgDD1Zk4Qb4p5 sbWZN/eUmwutVcCTmBb8TRs3EMfKvdI+VcP/7XTjSrLdSLRO9AY1dxfCCG/tihsNpCgq JM0XH35tVGrHIb4b8zFyr6GiYBmIw6Ldk5//Fbjp6vy2A/n1qhpnMTc14RvXc2ZUKwBj X8dUJi9CMiigv+A26LVfxyrwK2gDmmGlRpilD4/txYoo1z62SdON3feNeFLPx5cp1lSB DG9O0eKKZWPigqqTP/lSbxYpzpLRAvvIw44GAU3Ft1LCr6jWYhup1QifGb7qjVVtYZNJ CyrQ== 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=5PMt33dqXzIUjSsz8EwXzOq9fkust7oXx3uBnBL8KqI=; b=HLSv5IOS7a6yWPjFO0WqoSHtTQ/y/t/qgnefaOorv8akNDrEbScVjygbt5at7fTePX 1HStnMRSH52anO7voJhsEKPouITsf8q81vM4Y5qb/jJLLW2pD7f6dZ78SOsiXu7kU1Y5 VNMVVf3RB241HRJoPmGmqptor+AJ/0Q7Y2RoZre/QVdEuSkXVV0Ky7UIZRrCquAjJPMr /0GqOiaqMzn9zTsi81OLp/9tyDFhTwt+Y6jwYX8vehix1qxwK4+VJ0HiPoiY8ZHfu5Ft L0PqXdqWaNodJe0TkX4wkExpRi/2YXeT+KL7EjbjCy5JaHyY3jcOj/rZZsS7E36gq6L7 sZiw== X-Gm-Message-State: AOAM532lOPoozKQFtwgyVzUFu18gXauA3GU06jVEkMufP8d6N+5Qfctm UQh7GKwXAxF1sL6rxhuQbIP+HFbdko4I4g== X-Google-Smtp-Source: ABdhPJwTzdpDWtWdKCBbPVchdcvQwYCBgoUkdFSs2m3tdbJycUvw39vdTsyltRRVqyR7bTkpDdNfOA== X-Received: by 2002:a5d:4984:: with SMTP id r4mr28624353wrq.401.1596015123277; Wed, 29 Jul 2020 02:32:03 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id c15sm3709921wme.23.2020.07.29.02.32.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2020 02:32:02 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 29 Jul 2020 10:31:53 +0100 Message-Id: <20200729093154.12296-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200729093154.12296-1-david.plowman@raspberrypi.com> References: <20200729093154.12296-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/5] libcamera: raspberrypi: ALSC: Handle transform in colour tables 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" This commit updates ALSC (Auto Lens Shading Correction) to handle the transform now passed in the SwitchMode method. The transform is applied by the sensor, so the statistics we get already incorporates it, and the adpative algorithm is agnostic to it. That leaves only the calibrated tables (assumed measured without any user transform) that need to be flipped, and resample_cal_table - where we resample the calibrated table anyway - is an easy place to do it. --- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 22 ++++++++++++++------- src/ipa/raspberrypi/controller/rpi/alsc.hpp | 3 ++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp index 5fac9dd..33c1a88 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp @@ -151,8 +151,9 @@ static void get_cal_table(double ct, std::vector const &calibrations, double cal_table[XY]); static void resample_cal_table(double const cal_table_in[XY], - CameraMode const &camera_mode, - double cal_table_out[XY]); + CameraMode const &camera_mode, + Transform transform, + double cal_table_out[XY]); static void compensate_lambdas_for_cal(double const cal_table[XY], double const old_lambdas[XY], double new_lambdas[XY]); @@ -187,6 +188,7 @@ void Alsc::SwitchMode(CameraMode const &camera_mode, Transform transform, Metada // change, any effects should be transient, and if they're not transient // enough, we'll revisit the question then. camera_mode_ = camera_mode; + transform_ = transform; if (first_time_) { // On the first time, arrange for something sensible in the // initial tables. Construct the tables for some default colour @@ -194,9 +196,9 @@ void Alsc::SwitchMode(CameraMode const &camera_mode, Transform transform, Metada // adaptive algorithm. double cal_table_r[XY], cal_table_b[XY], cal_table_tmp[XY]; get_cal_table(4000, config_.calibrations_Cr, cal_table_tmp); - resample_cal_table(cal_table_tmp, camera_mode_, cal_table_r); + resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_r); get_cal_table(4000, config_.calibrations_Cb, cal_table_tmp); - resample_cal_table(cal_table_tmp, camera_mode_, cal_table_b); + resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_b); compensate_lambdas_for_cal(cal_table_r, lambda_r_, async_lambda_r_); compensate_lambdas_for_cal(cal_table_b, lambda_b_, @@ -379,7 +381,9 @@ void get_cal_table(double ct, std::vector const &calibrations, } void resample_cal_table(double const cal_table_in[XY], - CameraMode const &camera_mode, double cal_table_out[XY]) + CameraMode const &camera_mode, + Transform transform, + double cal_table_out[XY]) { // Precalculate and cache the x sampling locations and phases to save // recomputing them on every row. @@ -395,6 +399,8 @@ void resample_cal_table(double const cal_table_in[XY], xf[i] = x - x_lo[i]; x_hi[i] = std::min(x_lo[i] + 1, X - 1); x_lo[i] = std::max(x_lo[i], 0); + if (transform.contains(Transform::hflip())) + x_lo[i] = X - 1 - x_lo[i], x_hi[i] = X - 1 - x_hi[i]; } // Now march over the output table generating the new values. double scale_y = camera_mode.sensor_height / @@ -407,6 +413,8 @@ void resample_cal_table(double const cal_table_in[XY], double yf = y - y_lo; int y_hi = std::min(y_lo + 1, Y - 1); y_lo = std::max(y_lo, 0); + if (transform.contains(Transform::vflip())) + y_lo = Y - 1 - y_lo, y_hi = Y - 1 - y_hi; double const *row_above = cal_table_in + X * y_lo; double const *row_below = cal_table_in + X * y_hi; for (int i = 0; i < X; i++) { @@ -671,9 +679,9 @@ void Alsc::doAlsc() // Fetch the new calibrations (if any) for this CT. Resample them in // case the camera mode is not full-frame. get_cal_table(ct_, config_.calibrations_Cr, cal_table_tmp); - resample_cal_table(cal_table_tmp, async_camera_mode_, cal_table_r); + resample_cal_table(cal_table_tmp, async_camera_mode_, transform_, cal_table_r); get_cal_table(ct_, config_.calibrations_Cb, cal_table_tmp); - resample_cal_table(cal_table_tmp, async_camera_mode_, cal_table_b); + resample_cal_table(cal_table_tmp, async_camera_mode_, transform_, cal_table_b); // You could print out the cal tables for this image here, if you're // tuning the algorithm... (void)print_cal_table; diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.hpp b/src/ipa/raspberrypi/controller/rpi/alsc.hpp index 9b54578..9001e8a 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.hpp @@ -60,7 +60,8 @@ private: // configuration is read-only, and available to both threads AlscConfig config_; bool first_time_; - std::atomic camera_mode_; + CameraMode camera_mode_; + libcamera::Transform transform_; std::thread async_thread_; void asyncFunc(); // asynchronous thread function std::mutex mutex_; From patchwork Wed Jul 29 09:31:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9065 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 A6697BD86F for ; Wed, 29 Jul 2020 09:32:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 726DE61978; Wed, 29 Jul 2020 11:32:11 +0200 (CEST) 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="boVR00H7"; dkim-atps=neutral Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id ED0B761978 for ; Wed, 29 Jul 2020 11:32:04 +0200 (CEST) Received: by mail-wm1-x32c.google.com with SMTP id f18so2240311wml.3 for ; Wed, 29 Jul 2020 02:32:04 -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=ZtZWQ4r+KnY4SNVh2eWcER4lIk+ceFuugUZ27hqXcQ4=; b=boVR00H7Cs92h2wQ0+a9mbjZMIiqxdMEmKcWWcVTg9kVhHrSa70HgOlcRlNtp/vzXl LER60nkiH24gD2QHmDu2VNNb01f5ZEb4zpb0Rq1xVkPwvPJmI4FkWAdhl90iBSzGseQj +wMbhOgY/bNKngq9+FBW6F/dktekRjexT9I1oo4Om0bETPg0dG0SGeuRi97wobjrIe+j 000QHl7qXwR6cVhFCAe45nPMpbCa/yBPEBrVTTU+0vnCh6V9LBVK4ZPp9NPu7bx8xNNi S+Z+9ST/50hxjYFrHXUFwa4rA2EgM5NoNyIpwXV1uJtCZjx/tk53Abe3pu0p2u1Rz31I V1vA== 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=ZtZWQ4r+KnY4SNVh2eWcER4lIk+ceFuugUZ27hqXcQ4=; b=kifyuWQZluhyRgJLZd5lrcppoTAuYLTPfgeDB2sjaQsTQAoxWo1fzDYhp6CTKtc87a 7Nh8qBoWgXoD/2kvc1h+m0aN9JeqB26DZC1LXnqmODkEjiW80ij4iKzbNt0TU2hF7UCt 57iTrtv0xNsLoTBTF+dwNoBeV3rlFr/Nn6W801K6Fa9TiAI0AzD3wAgcXfueWi1mld7C YZ0B+rUFbi+DsJgngfJ4t18H5z+cgwDmg7Rzln7KNXIyTlFIlNEicIY4jeDrrA+LLAHZ Z+zyaWuQnOvZM5WsJsW2h7LKChLfiGiTnLQQDJESaJRIHFUPnkCTUHR2cBZamIn30rbT EU7g== X-Gm-Message-State: AOAM533qru8AJZW4AF4UPTkg/WYmuE3/Z2A+Vhx9DrSZmkavc34aOZZU /9w8kCtrBNtP4hn3HthBq8XKRcAeKmSJuA== X-Google-Smtp-Source: ABdhPJwwTk0TczdxlG65a/nxWq0MLRiSLGogIFnpgxOQptPRo19fEpxVZ2gOOD/5+suBNFzIzztMMw== X-Received: by 2002:a1c:283:: with SMTP id 125mr8301614wmc.12.1596015124196; Wed, 29 Jul 2020 02:32:04 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id c15sm3709921wme.23.2020.07.29.02.32.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2020 02:32:03 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Wed, 29 Jul 2020 10:31:54 +0100 Message-Id: <20200729093154.12296-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200729093154.12296-1-david.plowman@raspberrypi.com> References: <20200729093154.12296-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 5/5] libcamera: raspberrypi: ALSC: Fix crop/transform of luminance table 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" The code was not handling a change of crop correctly in the luminance table. This change fixes that, and also makes it work for a change of transform. As part of this it seemed sensible to ensure the asynchronous thread is paused in the SwitchMode, at which point it also makes sense to detect "significant" mode changes and to restart the whole ALSC algorithm when that happens. --- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 87 ++++++++++++++------- src/ipa/raspberrypi/controller/rpi/alsc.hpp | 3 +- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp index 33c1a88..fa53314 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp @@ -168,46 +168,79 @@ void Alsc::Initialise() RPI_LOG("Alsc"); frame_count2_ = frame_count_ = frame_phase_ = 0; first_time_ = true; - // Initialise the lambdas. Each call to Process then restarts from the - // previous results. Also initialise the previous frame tables to the - // same harmless values. - for (int i = 0; i < XY; i++) - lambda_r_[i] = lambda_b_[i] = 1.0; + ct_ = config_.default_ct; + // The lambdas are initialised in the SwitchMode. +} + +void Alsc::waitForAysncThread() +{ + std::unique_lock lock(mutex_); + if (async_started_) { + sync_signal_.wait(lock, [&] { + return async_finished_; + }); + async_started_ = false; + async_finished_ = false; + } +} + +static bool compare_crops(CameraMode const &cm0, CameraMode const &cm1) +{ + // Return true if the modes crop from the sensor significantly differently. + int left_diff = std::abs(cm0.crop_x - cm1.crop_x); + int top_diff = std::abs(cm0.crop_y - cm1.crop_y); + int right_diff = std::abs(cm0.crop_x + cm0.scale_x * cm0.width - + cm1.crop_x - cm1.scale_x * cm1.width); + int bottom_diff = std::abs(cm0.crop_y + cm0.scale_y * cm0.height - + cm1.crop_y - cm1.scale_y * cm1.height); + int threshold_x = cm0.sensor_width >> 4; + int threshold_y = cm0.sensor_height >> 4; + return left_diff > threshold_x || right_diff > threshold_x || + top_diff > threshold_y || bottom_diff > threshold_y; } void Alsc::SwitchMode(CameraMode const &camera_mode, Transform transform, Metadata *metadata) { (void)metadata; - // There's a bit of a question what we should do if the "crop" of the - // camera mode has changed. Any calculation currently in flight would - // not be useful to the new mode, so arguably we should abort it, and - // generate a new table (like the "first_time" code already here). When - // the crop doesn't change, we can presumably just leave things - // alone. For now, I think we'll just wait and see. When the crop does - // change, any effects should be transient, and if they're not transient - // enough, we'll revisit the question then. + // We're going to start over with the tables if there's any "significant" + // change. + bool reset_tables = first_time_ || transform_ != transform || + compare_crops(camera_mode_, camera_mode); + + // Ensure the other thread isn't running while we do this. + waitForAysncThread(); + camera_mode_ = camera_mode; transform_ = transform; - if (first_time_) { - // On the first time, arrange for something sensible in the - // initial tables. Construct the tables for some default colour - // temperature. This echoes the code in doAlsc, without the - // adaptive algorithm. + + // We must resample the luminance table like we do the others, but it's + // fixed so we can simply do it up front here. + resample_cal_table(config_.luminance_lut, camera_mode_, transform_, luminance_table_); + + if (reset_tables) { + // Upon every "table reset", arrange for something sensible to be + // generated. Construct the tables for the previous recorded colour + // temperature. In order to start over from scratch we initialise + // the lambdas, but the rest of this code then echoes the code in + // doAlsc, without the adaptive algorithm. + for (int i = 0; i < XY; i++) + lambda_r_[i] = lambda_b_[i] = 1.0; double cal_table_r[XY], cal_table_b[XY], cal_table_tmp[XY]; - get_cal_table(4000, config_.calibrations_Cr, cal_table_tmp); + get_cal_table(ct_, config_.calibrations_Cr, cal_table_tmp); resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_r); - get_cal_table(4000, config_.calibrations_Cb, cal_table_tmp); + get_cal_table(ct_, config_.calibrations_Cb, cal_table_tmp); resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_b); compensate_lambdas_for_cal(cal_table_r, lambda_r_, async_lambda_r_); compensate_lambdas_for_cal(cal_table_b, lambda_b_, async_lambda_b_); add_luminance_to_tables(sync_results_, async_lambda_r_, 1.0, - async_lambda_b_, config_.luminance_lut, + async_lambda_b_, luminance_table_, config_.luminance_strength); memcpy(prev_sync_results_, sync_results_, sizeof(prev_sync_results_)); + frame_phase_ = config_.frame_period; // run the algo again asap first_time_ = false; } } @@ -253,8 +286,8 @@ void Alsc::restartAsync(StatisticsPtr &stats, Metadata *image_metadata) { RPI_LOG("Starting ALSC thread"); // Get the current colour temperature. It's all we need from the - // metadata. - ct_ = get_ct(image_metadata, config_.default_ct); + // metadata. Use the previous value if none found. + ct_ = get_ct(image_metadata, ct_); // We have to copy the statistics here, dividing out our best guess of // the LSC table that the pipeline applied to them. AlscStatus alsc_status; @@ -269,8 +302,6 @@ void Alsc::restartAsync(StatisticsPtr &stats, Metadata *image_metadata) } copy_stats(statistics_, stats, alsc_status); frame_phase_ = 0; - // copy the camera mode so it won't change during the calculations - async_camera_mode_ = camera_mode_; async_start_ = true; async_started_ = true; async_signal_.notify_one(); @@ -679,9 +710,9 @@ void Alsc::doAlsc() // Fetch the new calibrations (if any) for this CT. Resample them in // case the camera mode is not full-frame. get_cal_table(ct_, config_.calibrations_Cr, cal_table_tmp); - resample_cal_table(cal_table_tmp, async_camera_mode_, transform_, cal_table_r); + resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_r); get_cal_table(ct_, config_.calibrations_Cb, cal_table_tmp); - resample_cal_table(cal_table_tmp, async_camera_mode_, transform_, cal_table_b); + resample_cal_table(cal_table_tmp, camera_mode_, transform_, cal_table_b); // You could print out the cal tables for this image here, if you're // tuning the algorithm... (void)print_cal_table; @@ -704,7 +735,7 @@ void Alsc::doAlsc() compensate_lambdas_for_cal(cal_table_b, lambda_b_, async_lambda_b_); // Fold in the luminance table at the appropriate strength. add_luminance_to_tables(async_results_, async_lambda_r_, 1.0, - async_lambda_b_, config_.luminance_lut, + async_lambda_b_, luminance_table_, config_.luminance_strength); } diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.hpp b/src/ipa/raspberrypi/controller/rpi/alsc.hpp index 9001e8a..63bf28f 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.hpp @@ -62,10 +62,10 @@ private: bool first_time_; CameraMode camera_mode_; libcamera::Transform transform_; + double luminance_table_[ALSC_CELLS_X * ALSC_CELLS_Y]; std::thread async_thread_; void asyncFunc(); // asynchronous thread function std::mutex mutex_; - CameraMode async_camera_mode_; // condvar for async thread to wait on std::condition_variable async_signal_; // condvar for synchronous thread to wait on @@ -88,6 +88,7 @@ private: int frame_count2_; double sync_results_[3][ALSC_CELLS_Y][ALSC_CELLS_X]; double prev_sync_results_[3][ALSC_CELLS_Y][ALSC_CELLS_X]; + void waitForAysncThread(); // The following are for the asynchronous thread to use, though the main // thread can set/reset them if the async thread is known to be idle: void restartAsync(StatisticsPtr &stats, Metadata *image_metadata);