From patchwork Fri Aug 28 14:41:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9416 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 1BCBABF019 for ; Fri, 28 Aug 2020 14:41:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 003DF628F3; Fri, 28 Aug 2020 16:41:18 +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="EbbwXOgh"; dkim-atps=neutral Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5EC326037B for ; Fri, 28 Aug 2020 16:41:17 +0200 (CEST) Received: by mail-wm1-x32f.google.com with SMTP id t14so1131711wmi.3 for ; Fri, 28 Aug 2020 07:41:17 -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=uSc5m7UZr/nBlCCvcn3e9RFGm3q8lICTUphQChO2UKs=; b=EbbwXOgh7mvpg8TRZU9V00by8tY4q1ISdQRzHZYGJRQMGRqYT9Xx0PHp6oGFDf/Uwo 2mmhe3dCFJx4bNrka4K8PcgRvQzyhPjdiMKJhzNCgcEBPH9NO5Ai/vH2r8+LQzzIZP12 78juB4WiSl+aFWmqdMSHxZAJlulVyRiCE/UICWRMU8cDphRi358oGGb9bIA4sgS7FmNy IOgaVwpbvxqCcCkkeatV1aJGXuKzCDk3kgcTt/8yto1q4iRm1i27kFCKvzt2u8h4uxip z8Q8gaYyXr0/Nivt6JjptANxdioDsVeAhy+iWTN2qEgMZT2CoDys0EmdD7jBGNTyYqd9 r6YA== 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=uSc5m7UZr/nBlCCvcn3e9RFGm3q8lICTUphQChO2UKs=; b=Q7jn6j4cBiWI6GOhAg/dw0hTLq0IoFcPQ8k7hX0hbGxgyDkbTduD8o94ySzZ/fj7Mh K0EEVNayBDSLRENRgGgeOmfjwCvzgclTGrHaSvvHvPojn9nCMO8+L0Dsce/G2UdOlB7y Ww4aJw7k70HbhTr/qBUAQKzxW6Fv8rBQAZVsUc/qfvUiV8bpplwLqdYhW9V4Q2Axsm1E oXxhfPBHxoFiF5g7Xtd7CyatPOGQLURhaiiPr+xn38F4/o186RXw6DSyS7MDvi9/lcBO UZLD2IpX4FcAj+MueXwCeXMFuxuiU5iWojAwrZt+JJW7r5OqEIxFPPVqro9hxo6nF0zi ZWFw== X-Gm-Message-State: AOAM5319X5ISdwmDK9NhGlMEm3yQk+wFgen30Jg7ytwcmTyI6XgLIjyG TKtFKnxzNYkbqC89MnurBxpgH9Ey7fg56A== X-Google-Smtp-Source: ABdhPJzeQJSBppjDipYMzWLIILRrQiR258LfjjI+Mkn/boxG/yPFpLTyEos4UKsSWlrYsFnbhLTAwA== X-Received: by 2002:a1c:b143:: with SMTP id a64mr1871961wmf.43.1598625676740; Fri, 28 Aug 2020 07:41:16 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:16 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:04 +0100 Message-Id: <20200828144110.17303-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 1/7] libcamera: pipeline: raspberrypi: Revert "Set sensor default orientation before configure()" 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 reverts commit 1e8c91b65695449c5246d17ba7dc439c8058b781. Now that we shall be implementing application-defined 2D transforms it's no longer possible to set the sensor orientation so early on. We have to wait until we have the CameraConfiguration object as that's where the application puts its choice of transform. Signed-off-by: David Plowman Reviewed-by: Kieran Bingham --- src/libcamera/pipeline/raspberrypi/raspberrypi.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index c1451e7..42c9caa 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -960,13 +960,6 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) /* Initialize the camera properties. */ data->properties_ = data->sensor_->properties(); - /* Configure the H/V flip controls based on the sensor rotation. */ - ControlList ctrls(data->unicam_[Unicam::Image].dev()->controls()); - int32_t rotation = data->properties_.get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); - data->unicam_[Unicam::Image].dev()->setControls(&ctrls); - /* * List the available output streams. * Currently cannot do Unicam streams! @@ -1171,6 +1164,13 @@ int RPiCameraData::configureIPA() { V4L2_CID_EXPOSURE, result.data[1] } }); sensorMetadata_ = result.data[2]; } + + /* 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)); + unicam_[Unicam::Image].dev()->setControls(&ctrls); } if (result.operation & RPI_IPA_CONFIG_SENSOR) { From patchwork Fri Aug 28 14:41:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9417 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 EE865BF019 for ; Fri, 28 Aug 2020 14:41:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BD946628EE; Fri, 28 Aug 2020 16:41:21 +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="jR73RrRH"; dkim-atps=neutral Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 844EE6291F for ; Fri, 28 Aug 2020 16:41:18 +0200 (CEST) Received: by mail-wm1-x332.google.com with SMTP id s13so1152463wmh.4 for ; Fri, 28 Aug 2020 07:41:18 -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=P2K+eKX7FaaNcA/AksWwAKgH6mqYW8ujEtMVhB6/Uqs=; b=jR73RrRHXamnBVtjRDEBSPg8GTGV/n+OZRLcBWPGaZYnu6rsQEOTHtKUPEthJv5s+B xmC60fgypjVkvQCdB/wNTC1A9YRBR///Hgy346ob/ArHZZ6h6j/QEBzZKVpbIyN2U0oX 4I4T8t18Fj1EoIw8k0ZPrOb0YZbrVlvbosIQW/TWYDZd4mvVPbFJF/rG1iC5fezI/wXD MRaVkfBgOgeMuarveKNQoQNKXTBdQOoBHy7x31LhLWNZ78fwprV083VPJFo7arkqeTJk 2yOEJejWDcIGqxDx6/NEbb1ebMKmvloVlPrjQ0dmNm3UiDUdJ+dDiE/p/N7HPl6tPyFW n8Kw== 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=P2K+eKX7FaaNcA/AksWwAKgH6mqYW8ujEtMVhB6/Uqs=; b=T1AeU/FwoSL0M8x49xb29eYd3BMQdMW+UlHIAUhlM8pAdY0p+vopjrFbUmAp6L8Bmq Xx0459Ac6EOPay/xBZj+nQruBVQ/ECNb4Mn7u2Mq1e6Cj0j1LLhcBEibyucD79xu84K1 CoDLc4/dHLjWWEQ4g+33SbYSs5rLCVVzBEnRiOWLVvgrzpU3yxBwH1lS4MTUVMRUu28j pH8rgqx6JIe/0klqC/QZO/YadG/KVlZtdnZfErZfmJ/TLMWguD4DFPZoS6z7FRzJ6MQ3 fupUNecGjliTx/GRWZxHrvjr2NQK0QoonzPBR3knYNcsYbu8WYFKLPIH5uenrpFHNNg5 bFrQ== X-Gm-Message-State: AOAM530COpVC2trnr1fpHWpj9hw3urJlB3k22Ktjs5af3BTsr28dWrCm SrRC8iwtNtwFSGH62cIivfuGAZFO4B4jgQ== X-Google-Smtp-Source: ABdhPJzNJkE2+QRHwN2O0QNLGN9LtY5gMk5aa5p1/x4MFOKRzdg5pzd4UXv1LzQVLljlxIS8vQpbRA== X-Received: by 2002:a7b:cf3a:: with SMTP id m26mr2065040wmg.25.1598625677736; Fri, 28 Aug 2020 07:41:17 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:17 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:05 +0100 Message-Id: <20200828144110.17303-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 2/7] libcamera: pipeline: raspberrypi: Set sensor orientation during validate 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" We set the sensor orientation (h and v flips) during validate as this will in general affect the Bayer order output by the sensor. Doing it here means that the correct raw format gets advertised in any raw streams that the application requested. Signed-off-by: David Plowman --- .../pipeline/raspberrypi/raspberrypi.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 42c9caa..7aace71 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -400,6 +400,17 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; + /* + * Configure the H/V flip controls based on the sensor rotation. We do + * this here so that the sensor has the correct Bayer format that will + * get advertised in the configuration of any raw streams. + */ + ControlList ctrls(data_->unicam_[Unicam::Image].dev()->controls()); + int32_t rotation = data_->sensor_->properties().get(properties::Rotation); + ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); + ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); + data_->unicam_[Unicam::Image].dev()->setControls(&ctrls); + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair outSize[2]; Size maxSize; @@ -1164,13 +1175,6 @@ int RPiCameraData::configureIPA() { V4L2_CID_EXPOSURE, result.data[1] } }); sensorMetadata_ = result.data[2]; } - - /* 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)); - unicam_[Unicam::Image].dev()->setControls(&ctrls); } if (result.operation & RPI_IPA_CONFIG_SENSOR) { From patchwork Fri Aug 28 14:41:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9418 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 8D8D3BF019 for ; Fri, 28 Aug 2020 14:41:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 34C356291C; Fri, 28 Aug 2020 16:41:22 +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="hGG4OygN"; dkim-atps=neutral Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9B42362919 for ; Fri, 28 Aug 2020 16:41:19 +0200 (CEST) Received: by mail-wm1-x32b.google.com with SMTP id b79so1128926wmb.4 for ; Fri, 28 Aug 2020 07:41:19 -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=cFfGFA5lLUYhZvc3kuLUmQgz4uF6DdWC68RCzSTtnRw=; b=hGG4OygNLr715khX5HOO6wmSSXDP3Sawe75+kDMWMisk8PCwTpBEAr7U62/Dy8y5Ck NkdPWkMCwoYLk9yZb1ASFpJKz/AUstR9FZ963VrYvlnanTQbBl+mtIDVvP60Xc9oIK5w QLmSe9a1HCzIjtJ4roCGcY2zaYa/y/C14/slRwn667pZeKxy2h0u/fbWoSHgPoNbqo7A 6Yz3g6jAcJJuFuxFdDGJ1SPB7TwxxcZ6Z5ltMS1XpQSH3mJ70+E5znw7E1kuBOyIrYmg euA6XypcD5h6tf5wcI+4Mm65LoBR75kaVNbbhgUsTf1sKw9wefntb1tVlLrPRRM32nai psbw== 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=cFfGFA5lLUYhZvc3kuLUmQgz4uF6DdWC68RCzSTtnRw=; b=jWGaOO8QxbPo0LKOLiMtVfiFSh7oy2e2OE9/wKuP3hZAF1sQO9Kpdp0xM+5CoxKCL1 Tewwk7jU4lzpSzk2cSPlr6HmXHhDONJu9PrOjLE/dFWH/zvzFcGJY7GXacHTpcCdqnHh ba1PR9acn3kC4DtvSf3iZSwUkDRHmDaL+jzwjm2roM/r1WOs+4WmEtvskcSCRkvyd7Im XeOVe8Jolptbre37NCEx2OOTkf3zJ2itgJ48IRJG69mWSDTV0eIpIbqFUDVQzEYWq/zl z+y4FQ9LQjGzhYonYd5R2uXaxr6RrnlPQehJeAgKg24sJzLXbyPRzHwMs2WjqR2QfziE LGXg== X-Gm-Message-State: AOAM532Z86ClaOWaPVtFycwepC4veBVUrC6mAV3xJVIjH445P5Ptz8a/ Tag8/xFWRCPOg2HIeIBieVngFm8i5JirvA== X-Google-Smtp-Source: ABdhPJxVh2I2vz7x+NU8EohaD88d1GTgARkp5YP4rwHtGitoxrhxjFWwLfhS/LdafZqNUQZJWBaKKQ== X-Received: by 2002:a05:600c:252:: with SMTP id 18mr1949204wmj.33.1598625678728; Fri, 28 Aug 2020 07:41:18 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:18 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:06 +0100 Message-Id: <20200828144110.17303-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/7] libcamera: Add Transform enum to represet 2D plane transforms. 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" We implement 2D transforms as an enum class with 8 elements, consisting of the usual 2D plane transformations (flips, rotations etc.). The transform is made up of 3 bits, indicating whether the transform includes: a transpose, a horizontal flip (mirror) and a vertical flip. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/meson.build | 1 + include/libcamera/transform.h | 73 ++++++++ src/libcamera/meson.build | 1 + src/libcamera/transform.cpp | 312 ++++++++++++++++++++++++++++++++++ 4 files changed, 387 insertions(+) create mode 100644 include/libcamera/transform.h create mode 100644 src/libcamera/transform.cpp 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..7d88937 --- /dev/null +++ b/include/libcamera/transform.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Limited + * + * transform.h - 2D plane transforms + */ + +#ifndef __LIBCAMERA_TRANSFORM_H__ +#define __LIBCAMERA_TRANSFORM_H__ + +#include + +namespace libcamera { + +enum class Transform : int { + Identity = 0, + Rot0 = Identity, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, + Rot180 = HVFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose +}; + +constexpr Transform operator&(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) & static_cast(t1)); +} + +constexpr Transform operator|(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) | static_cast(t1)); +} + +constexpr Transform operator^(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) ^ static_cast(t1)); +} + +constexpr Transform &operator&=(Transform &t0, Transform t1) +{ + return t0 = t0 & t1; +} + +constexpr Transform &operator|=(Transform &t0, Transform t1) +{ + return t0 = t0 | t1; +} + +constexpr Transform &operator^=(Transform &t0, Transform t1) +{ + return t0 = t0 ^ t1; +} + +Transform operator*(Transform t0, Transform t1); + +Transform operator-(Transform t); + +constexpr bool operator!(Transform t) +{ + return t == Transform::Identity; +} + +Transform transformFromRotation(int angle, bool *success = nullptr); + +const char *transformToString(Transform t); + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_TRANSFORM_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index af2f3d9..edec55e 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -44,6 +44,7 @@ libcamera_sources = files([ 'sysfs.cpp', 'thread.cpp', 'timer.cpp', + 'transform.cpp', 'utils.cpp', 'v4l2_controls.cpp', 'v4l2_device.cpp', diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp new file mode 100644 index 0000000..88798d4 --- /dev/null +++ b/src/libcamera/transform.cpp @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Limited + * + * transform.cpp - 2D plane transforms. + */ + +#include + +/** + * \file transform.h + * \brief Enum to represent and manipulate 2D plane transforms. + */ + +namespace libcamera { + +/** + * \enum Transform + * \brief Enum to represent a 2D plane transform. + * + * The Transform can take 8 distinct values, representing the usual 2D plane + * transforms listed below. Each of these transforms can be constructed + * out of 3 basic operations, namely a horizontal flip (mirror), a vertical + * flip, and a transposition (about the main diagonal). The transforms are + * encoded such that a single bit indicates the presence of each of the 3 + * basic operations: + * + * - bit 0 - presence of a horizontal flip + * - bit 1 - presence of a vertical flip + * - bit 2 - presence of a transposition. + * + * We regard these 3 basic operations as being applied in a specific order: + * first the two flip operations (actually they commute, so the order between + * them is unimportant) and finally any transpose operation. + * + * Functions are provided to manipulate directly the bits within the transform + * encoding, but there are also higher-level functions to invert and compose + * transforms. Transforms are composed according to the usual mathematical + * convention such that the right transform is applied first, and the left + * transform is applied second. + * + * Finally, we have a total of 8 distinct transformations, as follows (a + * couple of them have additional synonyms for convenience). We illustrate each + * with its nominal effect on a rectangle with vertices labelled A, B, C and D. + * + * **Identity** + * + * Identity transform. +~~~ + A-B A-B +Input image | | goes to output image | | + C-D C-D +~~~ + * Numeric value: 0 (no bits set). + * + * **Rot0** + * + * Synonym for `Identity` (zero degree rotation). + * + * **HFlip** + * + * Horizontal flip. +~~~ + A-B B-A +Input image | | goes to output image | | + C-D D-C +~~~ + * Numeric value: 1 (horizontal flip bit set only). + * + * **VFlip** + * + * Vertical flip. +~~~ + A-B C-D +Input image | | goes to output image | | + C-D A-B +~~~ + * Numeric value: 2 (vertical flip bit set only). + * + * **HVFlip** + * + * Horizontal and vertical flip (identical to a 180 degree rotation). +~~~ + A-B D-C +Input image | | goes to output image | | + C-D B-A +~~~ + * Numeric value: 3 (horizontal and vertical flip bits set). + * + * **Rot180** + * + * Synonym for `HVFlip` (180 degree rotation). + * + * **Transpose** + * + * Transpose (about the main diagonal). +~~~ + A-B A-C +Input image | | goes to output image | | + C-D B-D +~~~ + * Numeric value: 4 (transpose bit set only). + * + * **Rot270** + * + * Rotation by 270 degrees clockwise (90 degrees anticlockwise). +~~~ + A-B B-D +Input image | | goes to output image | | + C-D A-C +~~~ + * Numeric value: 5 (transpose and horizontal flip bits set). + * + * **Rot90** + * + * Rotation by 90 degrees clockwise (270 degrees anticlockwise). +~~~ + A-B C-A +Input image | | goes to output image | | + C-D D-B +~~~ + * Numeric value: 6 (transpose and vertical flip bits set). + * + * **Rot180Transpose** + * + * Rotation by 180 degrees followed by transpose (alternatively, transposition + * about the "opposite diagonal"). +~~~ + A-B D-B +Input image | | goes to output image | | + C-D C-A +~~~ + * Numeric value: 7 (all bits set). + * + * \sa https://en.wikipedia.org/wiki/Examples_of_groups#dihedral_group_of_order_8 + * + * The set of 2D plane transforms is also known as the symmetry group of a + * square, described in the link. Note that the group can be generated by + * only 2 elements (the horizontal flip and a 90 degree rotation, for + * example), however, the encoding used here makes the presence of the vertical + * flip explicit. + */ + +/** + * \fn operator &(Transform t0, Transform t1) + * \brief Apply bitwise AND operator between the bits in the two transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \fn operator |(Transform t0, Transform t1) + * \brief Apply bitwise OR operator between the bits in the two transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \fn operator ^(Transform t0, Transform t1) + * \brief Apply bitwise XOR operator between the bits in the two transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \fn operator &=(Transform &t0, Transform t1) + * \brief Apply bitwise AND-assignment operator between the bits in the two + * transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \fn operator |=(Transform &t0, Transform t1) + * \brief Apply bitwise OR-assignment operator between the bits in the two + * transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \fn operator ^=(Transform &t0, Transform t1) + * \brief Apply bitwise XOR-assignment operator between the bits in the two + * transforms. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + */ + +/** + * \brief Compose two transforms together. + * \param[in] t1 The second transform. + * \param[in] t0 The first transform. + * + * Composing transforms follows the usual mathematical convention for + * composing functions. That is, when performing `t1 * t0`, \a t0 is applied + * first, and then \a t1. + * For example, `Transpose * HFlip` performs `HFlip` first and then the + * `Transpose` yielding `Rot270`, as shown below. +~~~ + A-B B-A B-D +Input image | | -> HFLip -> | | -> Transpose -> | | = Rot270 + C-D D-C A-C +~~~ + * Note that composition is generally non-commutative for Transforms, + * and not the same as XOR-ing the underlying bit representations. + */ +Transform operator*(Transform t1, Transform t0) +{ + /* + * Reorder the operations so that we imagine doing t0's transpose + * (if any) after t1's flips. The effect is to swap t1's hflips for + * vflips and vice versa, after which we can just xor all the bits. + */ + Transform reordered = t1; + if (!!(t0 & Transform::Transpose)) { + reordered = t1 & Transform::Transpose; + if (!!(t1 & Transform::HFlip)) + reordered |= Transform::VFlip; + if (!!(t1 & Transform::VFlip)) + reordered |= Transform::HFlip; + } + + return reordered ^ t0; +} + +/** + * \brief Invert a transform. + * \param[in] t The transform to be inverted. + * + * That is, we return the transform such that `t * (-t)` and `(-t) * t` both + * yield the identity transform. + */ +Transform operator-(Transform t) +{ + /* All are self-inverses, except for Rot270 and Rot90. */ + static const Transform inverses[] = { + Transform::Identity, + Transform::HFlip, + Transform::VFlip, + Transform::HVFlip, + Transform::Transpose, + Transform::Rot90, + Transform::Rot270, + Transform::Rot180Transpose + }; + + return inverses[static_cast(t)]; +} + +/** + * \fn operator!(Transform t) + * \brief Return `true` if the transform is the `Identity`, otherwise `false`. + * \param[in] t The transform to be tested. + */ + +/** + * \brief Return the transform representing a rotation of the given angle + * clockwise. + * \param[in] angle The angle of rotation in a clockwise sense. Negative values + * can be used to represent anticlockwise rotations. + * \param[out] success Set to `true` if the angle is a multiple of 90 degrees, + * otherwise `false`. + * \return The transform corresponding to the rotation if \a success was set to + * `true`, otherwise the `Identity` transform. + */ +Transform transformFromRotation(int angle, bool *success) +{ + angle = angle % 360; + if (angle < 0) + angle += 360; + + if (success != nullptr) + *success = true; + + switch (angle) { + case 0: + return Transform::Identity; + case 90: + return Transform::Rot90; + case 180: + return Transform::Rot180; + case 270: + return Transform::Rot270; + } + + if (success != nullptr) + *success = false; + + return Transform::Identity; +} + +/** + * \brief Return a character string describing the transform. + * \param[in] t The transform to be described. + */ +const char *transformToString(Transform t) +{ + static const char *strings[] = { + "identity", + "hflip", + "vflip", + "hvflip", + "transpose", + "rot270", + "rot90", + "rot180transpose" + }; + + return strings[static_cast(t)]; +} + +} /* namespace libcamera */ From patchwork Fri Aug 28 14:41: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: 9419 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 4D2F4BF019 for ; Fri, 28 Aug 2020 14:41:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F34CE62924; Fri, 28 Aug 2020 16:41:22 +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="gx09iNxl"; dkim-atps=neutral Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2FB84628EE for ; Fri, 28 Aug 2020 16:41:20 +0200 (CEST) Received: by mail-wm1-x335.google.com with SMTP id t14so1131827wmi.3 for ; Fri, 28 Aug 2020 07:41: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=Yi+HeC4pTRSEz/O01Hb068ZFqRAOPwlmJOluXXvpfGU=; b=gx09iNxlynoeJfcUEosTarbxoG1sDZwA4NchIegY32qTd4FMp6WzLsfjoOEZey1DzS VQgJfB/wcwZRo5pdsLU4G2WSFsbK5RN6ZLsoS473xmas86+c84hS/1hpMLtLTIreafq5 7Uxgf1TcsoRPWaq52WXVi8NIAvAR/Q2pFmtxvxcPyvxgsvFN/hFqaEYNU6+R96qxS2l9 QgJJgcOgcxvn2CndjNT114Ve9mUi1K9pdvLYBTEOyc5lLpssI7B74devhgxp7OUsvhC8 ejSIfxCRTpfoR5pLuhcVImVD0Ja97JGq7SeDfcTofyy7CGVOugQbaTn/h06pV+J+6f3P fuhA== 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=Yi+HeC4pTRSEz/O01Hb068ZFqRAOPwlmJOluXXvpfGU=; b=Oq85P2lQ1YCnbeELCigmuY14xQWuLiBleziwFtDsafs4sgeh/7xl5Yq6/X501pv5K6 3NkGvJAQjSoTMrBCI6sWG2iUUoi+lMDgeD5lsr2pXycRVOckU4sAErwtjfWXdg7xBGEC EPkM0ZMuquNUmlKyvkQgp5RbUoYtChwrQAIvo7XQ5py2+wwuA1Ui7axPQY6ju5Ln1dnE Fp+eY+/VuLnefB9WGnG4mzbPsmQF8HPQs9JIinJWk8sd57NNFBQs2s/Ertadj+MHRpl5 Vo+4+0b7hgTDB2gH18aTbs1SQk1GMLSAYVhKMqMqe+uMLALbCILVnxa37qQyjRQZc3DI AOiQ== X-Gm-Message-State: AOAM532CVtRPFrHJWpAtWr39KivIlnplGmnCEIjGdFjO0cpvTYxi4zjx MXWFksPFW9lilj4oQx2tOyBe+jVsir1bbw== X-Google-Smtp-Source: ABdhPJwld2jiMlJc2YxRWeXA9uDoh8jZikDLVmZuyni3uUp3L96VFL7modgNzSx4VDqsV7c0LsASjQ== X-Received: by 2002:a1c:f204:: with SMTP id s4mr2028983wmc.9.1598625679511; Fri, 28 Aug 2020 07:41:19 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:19 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:07 +0100 Message-Id: <20200828144110.17303-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 4/7] libcamera: Add user Transform to CameraConfiguration 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 a field to the CameraConfiguration (including the necessary documentation) to represent a 2D transform requested by the application. All pipeline handlers are amended to coerce this to the Identity, marking the configuration as "adjusted" if something different had been requested. Pipeline handlers that support Transforms can be amended subsequently. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/camera.h | 3 +++ src/libcamera/camera.cpp | 16 +++++++++++++++- src/libcamera/pipeline/ipu3/ipu3.cpp | 5 +++++ .../pipeline/raspberrypi/raspberrypi.cpp | 5 +++++ src/libcamera/pipeline/rkisp1/rkisp1.cpp | 5 +++++ src/libcamera/pipeline/simple/simple.cpp | 5 +++++ src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 5 +++++ src/libcamera/pipeline/vimc/vimc.cpp | 5 +++++ 8 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 272c12c..a2ee4e7 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace libcamera { @@ -61,6 +62,8 @@ public: bool empty() const; std::size_t size() const; + Transform transform; + protected: CameraConfiguration(); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 4a9c19c..b547ffe 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -93,7 +93,7 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : config_({}) + : transform(Transform::Identity), config_({}) { } @@ -250,6 +250,20 @@ std::size_t CameraConfiguration::size() const return config_.size(); } +/** + * \var CameraConfiguration::transform + * \brief User-specified transform to be applied to the image + * + * The transform is a user-specified 2D plane transform that will be applied + * to the camera images by the processing pipeline before being handed to + * the application. This is subsequent to any transform that is already + * required to fix up any platform-defined rotation. + * + * The usual 2D plane transforms are allowed here (horizontal/vertical + * flips, multiple of 90-degree rotations etc.), but the validate() function + * may adjust this field at its discretion if the selection is not supported. + */ + /** * \var CameraConfiguration::config_ * \brief The vector of stream configurations diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 2d881fe..22b8825 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -138,6 +138,11 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > IPU3_MAX_STREAMS) { config_.resize(IPU3_MAX_STREAMS); diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 7aace71..dc36f53 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -400,6 +400,11 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* * Configure the H/V flip controls based on the sensor rotation. We do * this here so that the sensor has the correct Bayer format that will diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 4d89aab..6f53a1d 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -478,6 +478,11 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index eb72e3b..10223a9 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -438,6 +438,11 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index bafe6f1..ba0efc8 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -109,6 +109,11 @@ CameraConfiguration::Status UVCCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index d192670..fc8085f 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -130,6 +130,11 @@ CameraConfiguration::Status VimcCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); From patchwork Fri Aug 28 14:41:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9420 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 012C9BF019 for ; Fri, 28 Aug 2020 14:41:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BD8B8628EE; Fri, 28 Aug 2020 16:41:24 +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="ApAztjra"; dkim-atps=neutral Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 21B6E62919 for ; Fri, 28 Aug 2020 16:41:21 +0200 (CEST) Received: by mail-wm1-x329.google.com with SMTP id o21so1147145wmc.0 for ; Fri, 28 Aug 2020 07:41:21 -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=5RQ8Tgj+C3/uKLvM1ptBLXMv3MS0gQ7Sq45idn4oLKg=; b=ApAztjraLYr+3faisoVdCNOg2X8Py+eGRLufcnbvR9e1BGr5lmVRGdcb80K9Xq/rKp YOM0kfTzY4Em9/gd8p1TK/FSKPQHaUwd47kAUJ2f4VBZYO9OCBZX95gYoFe/netsD3na zObHrWAwO/nwBT0OtPJVOpL/2ga99v4oN3v6k58Dt2P5AiNCb271uWp8u3r7OhDtPpUe cS0l/N2hPccqKfWHBTIU/80ijc/E3NhoQ2Dy5+kWS0BGZwD5lkc4IbdhFBdklgfwc+At zKC+8aZ176rM6jmMZ2K9QsmkdrK8Am6So1KCJ6hOJGsAf58930qdUnflPfFn24E8C61g 9N8g== 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=5RQ8Tgj+C3/uKLvM1ptBLXMv3MS0gQ7Sq45idn4oLKg=; b=lMN3C+yXHpT2C+1PhlEFZQtIOSIpYLe6WDlqDUIKqi+N19khwo/wQz7lqD5jiqUK7h b1wCWO1dCi1zJGbiQ3uPc/V8yc+DyA6Y9TqS16A/5EYmytZSyzZICNK590WqD82iw2dw Ypl9P1Na4YMzLN7X+X7WYTgnexaaw2bpPVQRSGIrj8KBfvlhbwfJrAq+G5YjGXzIpP4T S0F3+68CrAUhnSm+Hs1FlAK56zon/HgSqZ3xI/fpmxWCVGHez+xnUcs/DvPFtqJckGEO JIYiYrKeuVIa67PeprrOxH1yrlM7I4aVXXKSHpFndDENp3suhzwiX3VtlzObiVkHIXxf u1wQ== X-Gm-Message-State: AOAM530hT3aaqhDie/HeEbZy46F8VkHgX4XneHRB/JIy5vfnAmfkudts SzvwyjiLXZsg1MKfZPrhhzx47o5WyWfkkQ== X-Google-Smtp-Source: ABdhPJwMb30vppxLRTVGB+HspBbQcQPPjZqC7mUKxAr9v07ZzWWpVo5ySB4ZjbK6WegdsBKWzvO4Vg== X-Received: by 2002:a1c:2501:: with SMTP id l1mr2148725wml.16.1598625680577; Fri, 28 Aug 2020 07:41:20 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:19 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:08 +0100 Message-Id: <20200828144110.17303-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 5/7] libcamera: raspberrypi: Set camera flips correctly from user transform 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 Raspberry Pi pipeline handler allows all transforms except those involving a transpose. The user transform is combined with any inherent rotation of the camera, and the camera's H and V flip bits are set accordingly. Signed-off-by: David Plowman --- .../pipeline/raspberrypi/raspberrypi.cpp | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index dc36f53..6ea1432 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -400,20 +400,46 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + /* + * What if the platform has a non-90 degree rotation? We can't even + * "adjust" the configuration and carry on. Alternatively, raising an + * error means the platform can never run. Let's just print a warning + * and continue regardless; the rotation is effectively set to zero. + */ + int32_t rotation = data_->sensor_->properties().get(properties::Rotation); + bool success; + Transform combined = transform * transformFromRotation(rotation, &success); + if (!success) + LOG(RPI, Warning) << "Invalid rotation of " << rotation + << " degrees - ignoring"; + + /* + * We combine the platform and user transform, but must "adjust away" + * any combined result that includes a transform, as we can't do those. + * In this case, flipping only the transpose bit is helpful to + * applications - they either get the transform they requested, or have + * to do a simple transpose themselves (they don't have to worry about + * the other possible cases). + */ + if (!!(combined & Transform::Transpose)) { + /* + * Flipping the transpose bit in "transform" flips it in + * combined result too (as it's the last thing that happens). + */ + transform ^= Transform::Transpose; + combined ^= Transform::Transpose; status = Adjusted; } /* - * Configure the H/V flip controls based on the sensor rotation. We do - * this here so that the sensor has the correct Bayer format that will - * get advertised in the configuration of any raw streams. + * Configure the H/V flip controls based on the combination of the + * sensor rotation and the user transform. We do this here so that the + * sensor has the correct Bayer format that will get advertised in the + * configuration of any raw streams. */ ControlList ctrls(data_->unicam_[Unicam::Image].dev()->controls()); - int32_t rotation = data_->sensor_->properties().get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); + ctrls.set(V4L2_CID_HFLIP, static_cast(!!(combined & Transform::HFlip))); + ctrls.set(V4L2_CID_VFLIP, static_cast(!!(combined & Transform::VFlip))); data_->unicam_[Unicam::Image].dev()->setControls(&ctrls); unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; From patchwork Fri Aug 28 14:41:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9421 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 68B14BF019 for ; Fri, 28 Aug 2020 14:41:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 30A3C62927; Fri, 28 Aug 2020 16:41:25 +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="qUZNgImn"; dkim-atps=neutral Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 49E0462922 for ; Fri, 28 Aug 2020 16:41:22 +0200 (CEST) Received: by mail-wm1-x342.google.com with SMTP id q9so1162826wmj.2 for ; Fri, 28 Aug 2020 07:41:22 -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=lUHJcrqZxYousXRKyn9ExX5bvnUtThtzvf+6uU/OEKI=; b=qUZNgImn+XbouHvmr2MVr25mEVZjtZKyspkpcSoRg7/JpKGzvY9CXjbMHxAotT6mze Uve7aS5rYvzNBi0gE92ulV5q6tJNRtEUQoZBUhZbn8H+sjibnz9q30oo9s7BPiHbI88I 1VXhVgF8mQDIaT75S3csQRMcIWuUb2CejlltoYYsnva177xDEpFJ3Rh1zzKS3Ek3IrCV VCi28GTP8bcRUC3N/+8KqM5ek70kaGfIhosR+8Wd1mLV8TVZGXwXrKeuGr/OYNYizvuo XX0mboFg6YalABqp6MYlDt1z+cnhEu+IHC20rRJQsX5OYrIFI+DrWfO2iP4r3MtUB58h qFFg== 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=lUHJcrqZxYousXRKyn9ExX5bvnUtThtzvf+6uU/OEKI=; b=VEn9460lc41RZsERaM8+r0+fMioT6jnEeELmQUOaIc1wfPDh6NDgSxdOsgs99ETs6c fODgtC/OKvBnjqBuEF80Nf5k+yVHfNehdCNlQ4WjM3EcP9mSK0WlfViD2PjAd7XBJdwi RJLuG5wx9QeWmvRYDdj4+EDWbPxcztMj3m+/9b6T7IDcM/xt8ExEGggSqICB0elsPKVJ NPaY6saR6T/36EoEQxTguYYCV9wjgVBI39KUHcL7HiNkX9/fucB6dv9/1UWjkhkYRq2H 4a67y+NLKA83RVcvS9bSNl3NTb9qFC0Zt6oSlUMs3bCsrLd3XXRRGQ3QNP6w8pOKlKls 4j1A== X-Gm-Message-State: AOAM530PyehknIdHvaksRu6VBZGIWJxR5pDjc5AGnaC9fDhrN1L8MCTZ HIdhkYXWWn1EKEscw/PCkBcBO0HwOklIrw== X-Google-Smtp-Source: ABdhPJxyrbLqyp1BeYsRx1+QDzToTIKqi8Xib5cQMIwhi7kelospPaczMkk1lUcIZKQ8B4GsFyyS4g== X-Received: by 2002:a1c:5f06:: with SMTP id t6mr1950945wmb.54.1598625681513; Fri, 28 Aug 2020 07:41:21 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:20 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:09 +0100 Message-Id: <20200828144110.17303-7-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 6/7] libcamera: raspberrypi: Plumb user transform 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" This commit plumbs the user transform from the Raspberry Pi pipeline handler through to the IPA. Note that the transform is actually handled in the sensor (by setting the h/v flip bits), so the IPAs need to understand the orientation of the image they receive. Once in the IPA we add it to the CameraMode description, so that it becomes automatically available to all the individual control algorithms. The IPA configure method has to be reordered just a little so as to fill in the transform in the camera mode before calling SwitchMode. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- src/ipa/raspberrypi/controller/camera_mode.h | 4 ++ src/ipa/raspberrypi/raspberrypi.cpp | 48 +++++++++++-------- .../pipeline/raspberrypi/raspberrypi.cpp | 11 +++-- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/ipa/raspberrypi/controller/camera_mode.h b/src/ipa/raspberrypi/controller/camera_mode.h index 875bab3..920f11b 100644 --- a/src/ipa/raspberrypi/controller/camera_mode.h +++ b/src/ipa/raspberrypi/controller/camera_mode.h @@ -6,6 +6,8 @@ */ #pragma once +#include + // Description of a "camera mode", holding enough information for control // algorithms to adapt their behaviour to the different modes of the camera, // including binning, scaling, cropping etc. @@ -33,6 +35,8 @@ struct CameraMode { double noise_factor; // line time in nanoseconds double line_length; + // any camera transform *not* reflected already in the camera tuning + libcamera::Transform transform; }; #ifdef __cplusplus diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 4557016..c730326 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -232,6 +232,33 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, /* Re-assemble camera mode using the sensor info. */ setMode(sensorInfo); + /* + * The ipaConfig.data always gives us the user transform first. Note that + * this will always make the LS table pointer (if present) element 1. + */ + mode_.transform = static_cast(ipaConfig.data[0]); + + /* Store the lens shading table pointer and handle if available. */ + if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { + /* Remove any previous table, if there was one. */ + if (lsTable_) { + munmap(lsTable_, MAX_LS_GRID_SIZE); + lsTable_ = nullptr; + } + + /* Map the LS table buffer into user space (now element 1). */ + lsTableHandle_ = FileDescriptor(ipaConfig.data[1]); + if (lsTableHandle_.isValid()) { + lsTable_ = mmap(nullptr, MAX_LS_GRID_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, lsTableHandle_.fd(), 0); + + if (lsTable_ == MAP_FAILED) { + LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; + lsTable_ = nullptr; + } + } + } + /* Pass the camera mode to the CamHelper to setup algorithms. */ helper_->SetCameraMode(mode_); @@ -280,27 +307,6 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, } lastMode_ = mode_; - - /* Store the lens shading table pointer and handle if available. */ - if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { - /* Remove any previous table, if there was one. */ - if (lsTable_) { - munmap(lsTable_, MAX_LS_GRID_SIZE); - lsTable_ = nullptr; - } - - /* Map the LS table buffer into user space. */ - lsTableHandle_ = FileDescriptor(ipaConfig.data[0]); - if (lsTableHandle_.isValid()) { - lsTable_ = mmap(nullptr, MAX_LS_GRID_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, lsTableHandle_.fd(), 0); - - if (lsTable_ == MAP_FAILED) { - LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; - lsTable_ = nullptr; - } - } - } } void IPARPi::mapBuffers(const std::vector &buffers) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 6ea1432..8d8e249 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -294,7 +294,7 @@ public: void frameStarted(uint32_t sequence); int loadIPA(); - int configureIPA(); + int configureIPA(CameraConfiguration *config); void queueFrameAction(unsigned int frame, const IPAOperationData &action); @@ -793,7 +793,7 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) crop.y = (sensorFormat.size.height - crop.height) >> 1; data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); - ret = data->configureIPA(); + ret = data->configureIPA(config); if (ret) LOG(RPI, Error) << "Failed to configure the IPA: " << ret; @@ -1149,7 +1149,7 @@ int RPiCameraData::loadIPA() return ipa_->init(settings); } -int RPiCameraData::configureIPA() +int RPiCameraData::configureIPA(CameraConfiguration *config) { std::map streamConfig; std::map entityControls; @@ -1171,6 +1171,9 @@ int RPiCameraData::configureIPA() entityControls.emplace(0, unicam_[Unicam::Image].dev()->controls()); entityControls.emplace(1, isp_[Isp::Input].dev()->controls()); + /* Always send the user transform to the IPA. */ + ipaConfig.data = { static_cast(config->transform) }; + /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ if (!lsTable_.isValid()) { lsTable_ = dmaHeap_.alloc("ls_grid", MAX_LS_GRID_SIZE); @@ -1179,7 +1182,7 @@ int RPiCameraData::configureIPA() /* Allow the IPA to mmap the LS table via the file descriptor. */ ipaConfig.operation = RPI_IPA_CONFIG_LS_TABLE; - ipaConfig.data = { static_cast(lsTable_.fd()) }; + ipaConfig.data.push_back(static_cast(lsTable_.fd())); } CameraSensorInfo sensorInfo = {}; From patchwork Fri Aug 28 14:41:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9422 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 D40D9BF019 for ; Fri, 28 Aug 2020 14:41:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A1F176291C; Fri, 28 Aug 2020 16:41:25 +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="VwwNvkMQ"; 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 11254628F3 for ; Fri, 28 Aug 2020 16:41:23 +0200 (CEST) Received: by mail-wm1-x341.google.com with SMTP id t14so1132007wmi.3 for ; Fri, 28 Aug 2020 07:41:23 -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=yqzzfDnOwH0Awzrjn/UOHYIemK1WmVNHkZJX33QDD+s=; b=VwwNvkMQdERJJyklkxtJ7tfuiRdOWY3LTtCGpXMr27vSDGXgVFV1+VijMM4HdnpDGS E5/Wuukod1gHpJ3k/lyw3T430EHhwoAAik9upH9rzG8lSPPSQ0mceaxhm65WyaKpGHaj 4DWkuGqgP89BsnQszxD1GB19+yV//dsklca1VcUGOCIDAgYP+V+iuuvAjIl9S4zSWM6g VAgd32cfqD2RtKsLbCWqiEXaspZZ/w+Yum4jeGS6h7fc9JNw46tVyOYVa++ODeP8UFu/ 7j+3OOS2inQUUYn3MIYgPJcQckZi1MwbHJPIs/4hjFXK4adLXyMXccmaiQfvoa+llIg5 d5iw== 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=yqzzfDnOwH0Awzrjn/UOHYIemK1WmVNHkZJX33QDD+s=; b=YR5Rdbyb/DLIW6z6TCZSKSJklznHOC2WJwoHAJtNkE6saXWkN1C4QgoVdJsbm/D3AG mTRefH2xI2zI3gyKtgGxLGkjXUPkaQPtgS4hAsJ1mhG4KXrG/vez3K8i5H1+lsG7vrGP l7n+i8PekIIqVE30E7DDiHD8Y4oEO+K24GwL0g10GoIuVilk6ZHj27UyIzZwkTHSF7ZA pABrP94Gp0VzD/u0DejwXifFHdStaj9pg6ePjP880wrLxj6uPeAHLpDGDsm1VmVY5EpN ZVgm0Qq87oSkc0CeQkMKxQ3W4fWJmNUDm8Ty8bUjOiZaSmDb3aY8acSdD6hEiVOXKZFP 0a/g== X-Gm-Message-State: AOAM531+J8jynYVD9bI/kPx3L8pO1Gakrc4TOX3vE0aVKyNzTi95bmVt 3PIiYWo1SGZ59h7b542FCRf1Deo3CJil9A== X-Google-Smtp-Source: ABdhPJybbLoH3jpI5AH+zFSEaAQfwcLRkTxnnWNumnDYx4FwqQ0TCg4kOb6/uWqL54Ivs46wuWDGzg== X-Received: by 2002:a7b:c2aa:: with SMTP id c10mr1924450wmk.86.1598625682532; Fri, 28 Aug 2020 07:41:22 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id s12sm2493724wmj.26.2020.08.28.07.41.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2020 07:41:21 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 28 Aug 2020 15:41:10 +0100 Message-Id: <20200828144110.17303-8-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200828144110.17303-1-david.plowman@raspberrypi.com> References: <20200828144110.17303-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 7/7] libcamera: ipa: raspberrypi: ALSC: Handle user transform 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" Update ALSC (Auto Lens Shading Correction) to handle correctly the user transform now passed in the camera mode. The user transform is applied directly in the sensor so the image statistics already incorporate it, and the adaptive algorithm is entirely agnostic towards it, so all we have to do is flip the calibrated tables to match. (These tables will have been calibrated without the user transform.) Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp index 0d0e0b0..f610de2 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp @@ -184,7 +184,10 @@ void Alsc::waitForAysncThread() static bool compare_modes(CameraMode const &cm0, CameraMode const &cm1) { - // Return true if the modes crop from the sensor significantly differently. + // Return true if the modes crop from the sensor significantly differently, + // or if the user transform has changed. + if (cm0.transform != cm1.transform) + return true; int left_diff = abs(cm0.crop_x - cm1.crop_x); int top_diff = abs(cm0.crop_y - cm1.crop_y); int right_diff = fabs(cm0.crop_x + cm0.scale_x * cm0.width - @@ -428,6 +431,10 @@ 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 (!!(camera_mode.transform & libcamera::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 / @@ -440,6 +447,10 @@ 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 (!!(camera_mode.transform & libcamera::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++) {