From patchwork Fri Aug 21 15:56:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9349 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 A1AFCBE173 for ; Fri, 21 Aug 2020 15:57:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6EED2620AD; Fri, 21 Aug 2020 17:57:06 +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="TTiIiZMa"; dkim-atps=neutral Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B6E1661B4F for ; Fri, 21 Aug 2020 17:57:03 +0200 (CEST) Received: by mail-wr1-x436.google.com with SMTP id z18so2376428wrm.12 for ; Fri, 21 Aug 2020 08:57: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=higwrRx86y1VCnBIBUcZN8IjvAwGCz1fFp4qfbPNB4Q=; b=TTiIiZMa5tEo7I91UdFoYPQF+kgYB2KpCeD+03ubOFt7qgQwpKBIGUVJyDnJm8God7 u475Eebk2Pub3KwhWIvqtPxMi5RBtpv3LWNcp/MkX+FxqcVBsTsaZb72W0frBQQeI8pO wANsWnyEewBfjw2+vXFpjczk6NsME0kH/UgehOXuxnh9FiPIZOke2fy4Bb1SQ+ZqdoX4 82iCoKqgDa8dM13pG5Uc8hVF9tzZiYzKNdHAJETQyoqryP24LZqrgqgBEMuYrdvaH04D SRPLbeMfXkbOJm5+ff2tKZX5JZoW+5KLik43F39uOedtoSywCd3H5bh4xltJzcn/02Kn dBEg== 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=higwrRx86y1VCnBIBUcZN8IjvAwGCz1fFp4qfbPNB4Q=; b=h/5yS5xN7p8HAm/wr6RUnLYTgGjMA73CZiKDWxKJSdZiOr/93W86SZYXTS6wuPbEDg mYV6fk31uvritazMpCf8JHIEO64NZSVHat1b8vHYyciq8+YZBnK9GDLysAM0ef1CkB9D I/X2qqBZH+4GQwNlW9KadYuMEiwY7vpO7xNoM70YTfdUxunx+i73+vOJsSdH7XMbxos6 Ot6sBJRBBrvwckFI37z96c3PuyVTTYu7vKL6PUNg9OEg2XqSvHX37VNHjKaSO3fX6S2Y Ht/KohNwaO7nFG3xmIK3qOajimeEjASHSi44ZXjik/Wb7ax3mFFydHj4Qujppj1Fmm8R dB+w== X-Gm-Message-State: AOAM531jl+sLKDqqxxSWmedTyBKA2CmXXfJ5DeKykhnDxy5Nxk6+bot1 WW5pQ6g0pp7HbAZM1Z2RZ2ANa/aMfcyGsw== X-Google-Smtp-Source: ABdhPJw+8/Bs2JnM3pixa3IPw3sRLlQHDpL4tAjNsGOV342QUGt4kgdqEqfpVusCkYWNuWuEpZhPdA== X-Received: by 2002:adf:b18d:: with SMTP id q13mr3257691wra.256.1598025422872; Fri, 21 Aug 2020 08:57:02 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id l11sm5627385wme.11.2020.08.21.08.57.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Aug 2020 08:57:02 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 21 Aug 2020 16:56:37 +0100 Message-Id: <20200821155641.11839-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200821155641.11839-1-david.plowman@raspberrypi.com> References: <20200821155641.11839-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/5] 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 --- include/libcamera/meson.build | 1 + include/libcamera/transform.h | 73 +++++++++ src/libcamera/meson.build | 1 + src/libcamera/transform.cpp | 301 ++++++++++++++++++++++++++++++++++ 4 files changed, 376 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..2944efc --- /dev/null +++ b/src/libcamera/transform.cpp @@ -0,0 +1,301 @@ +/* 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 atomic 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 + * atomic operations: + * + * bit 0 - presence of a horizontal flip\n + * bit 1 - presence of a vertical flip\n + * bit 2 - presence of a transposition. + * + * We regard these 3 atomic 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). + */ + +/** + * \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. t1 is applied first, then t0. + * \param[in] t0 The first transform. + * \param[in] t1 The second transform. + * + * 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 t0, Transform t1) +{ + /* + * Reorder the operations so that we imagine doing t1's transpose + * (if any) after t0's flips. The effect is to swap t0's hflips for + * vflips and vice versa, after which we can just xor all the bits. + */ + Transform reordered = t0; + if (!!(t1 & Transform::Transpose)) { + reordered = t0 & Transform::Transpose; + if (!!(t0 & Transform::HFlip)) + reordered |= Transform::VFlip; + if (!!(t0 & Transform::VFlip)) + reordered |= Transform::HFlip; + } + + return reordered ^ t1; +} + +/** + * \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 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 21 15:56:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9350 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 6BE69BE173 for ; Fri, 21 Aug 2020 15:57:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B63D5626E7; Fri, 21 Aug 2020 17:57:06 +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="m7i822g6"; 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 6381361B4F for ; Fri, 21 Aug 2020 17:57:04 +0200 (CEST) Received: by mail-wm1-x32c.google.com with SMTP id k20so2311383wmi.5 for ; Fri, 21 Aug 2020 08:57: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=ooqtzkJFXjnQKZhOOrqOp0BexG8/MK24EDC4XFL8O00=; b=m7i822g66kP2hmjghihALWSQgedEAoRoODajcIiVucLJoFWFLNhAo3KHuzkz2ui6kg Jnl6bOrRRHbhAspef4XJ4TuvsJ2XkIsOI0u7A7WvfEAAScNz6lPQWh5Pho9GovEfTXVV ak+tZ0EVE4Er8RbRIx4At8/9htmaWCC+D27Zuu3anj5YyaaLZSJuLfj6+OgeRKT9Mz6E +leOa46BuFnHO+M6W1Z9eQwwgI4omyqg94Ww/uVjn/0nAUBMFt85RDECXvMD+/IgSjgf 14YP3pQtESoAMiuA0JiDEPNgqvy5BnKqLHSmcAmihPTMKrMxxMetcObIuDQaYiw1+mGW nJAg== 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=ooqtzkJFXjnQKZhOOrqOp0BexG8/MK24EDC4XFL8O00=; b=mAhGoHliG8Q84G3S6lhgq6oo6Ys2C1+Jt5DuTSZX6bwN+N7uuMuIR4psJ/X6BplAv1 gNqV6Knv3wlGaGstWjeCGhTQTf4SFs6scWfHE5xPtu2j5HTeAKr1vHO/opiPukWdWeJe sb/R8UYY4SHUxv+NIPnThZpD0kDOnqpRfNjAH1uxP3kIzFVviCmNNxPKNipoEi8v/tyo oCNjYioSESdSFhgEr4bP69178RziLFVSqxfnOq2vl78LsnCncbsjC1/K9m4pjwDwbKzE 2C8oiBR4EfMr3YZ18Ux9bVgzgmx5ToHgwO1j0xA3/IxUNKB8hgosHsyWhxGt38E9PUU7 +eqg== X-Gm-Message-State: AOAM530dH5ZAKGzf8jACTUmsZQxTpPcfLg+i0uBjxzYKk2Ii5HN6G394 u0VaRprMCL6GJHd4lMAgNM5O5VwLC1AFCw== X-Google-Smtp-Source: ABdhPJyJut0W5gVh1txcE4oPJ8xy7g7EH7BvED0phw0knmlueKaptK7dHo0WeM0OFKb858qZI73gUA== X-Received: by 2002:a1c:1d92:: with SMTP id d140mr3661238wmd.157.1598025423593; Fri, 21 Aug 2020 08:57:03 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id l11sm5627385wme.11.2020.08.21.08.57.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Aug 2020 08:57:03 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 21 Aug 2020 16:56:38 +0100 Message-Id: <20200821155641.11839-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200821155641.11839-1-david.plowman@raspberrypi.com> References: <20200821155641.11839-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/5] 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 --- 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..e12c1a0 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 pipeline handlers may + * adjust this field at their 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 019e50b..0f5ad73 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 eeaf335..236aa5c 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; + } + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair outSize[2]; Size maxSize; diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 32fdaed..f846733 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 bc892ec..fd14248 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -108,6 +108,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 cf244f1..bb791d6 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 21 15:56:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9351 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 D2501BE174 for ; Fri, 21 Aug 2020 15:57:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1CEFD626D3; Fri, 21 Aug 2020 17:57: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="dmpYDFhn"; dkim-atps=neutral Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 357B661B4F for ; Fri, 21 Aug 2020 17:57:05 +0200 (CEST) Received: by mail-wm1-x334.google.com with SMTP id g75so2316877wme.4 for ; Fri, 21 Aug 2020 08:57:05 -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=yc0n4NwlChACJCfwMFnscjpJL7xovCWYjrqOiaomR8Y=; b=dmpYDFhnYSonRiSE32rqFQWMmkiKCaO1OCkkJXllooy28+uZGMO3hYTZxc4Qn8GkjM 68TW0H2nZMp2VocU9yaJV/IRrWB2+hR8nUw7mIWaQVj1RY+iqQwMu0ZoG7o7/fIoyZYt k25jbsqSPGXv1iJ6bb/LhXDNp11KYONXVXAjPGB5qONht2mJ6iIVpNEQq12Epe1XgEsR JUI/K5AycfFrJJXobGOQm0CcUAvT489JVQZ1axJ8BPWwGG+pvOiHpD/Ye6ticnUX3VTQ RJswvF81tGQYgIyNZ2zWErtQ+2WeZFTD1+yFhvJSxI80x+lBSSczkCFggOLa1PHbv1Zt p66Q== 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=yc0n4NwlChACJCfwMFnscjpJL7xovCWYjrqOiaomR8Y=; b=VSUTftc2rTls2g5nfnUlTu0lkvbiL+wCwXuq5MUTdjv1X3JIijAUQ/1OHEBJjKFlDR 9ndxJZ34NvtcwGkn5XB8hENwoWDAsWzOq1FDfEK/m9ntK/Tc7z5qUbRmX+twt204jFSH XSUHZujfbm/fDQxcNnrUu5kT2XJ7ZhHDqKasJvZZq4GPxVtwWzjVaOJtb/AF2LkZrkpS 8Ax8J1sdB4/hRhLvfhWaJLvX0Gv4GmilHrXzP9c16SKxcnNXK05baWVZOPZlAXHnGZr6 23V68VVyBGXpmIJJAZ8aEl+cn08CLuUempbL+3U+9AfaCiBX4lHcLppBCM2HgnKv7ots mA0A== X-Gm-Message-State: AOAM533pbSqVUZQCLPqWtErPxyOXQanR7DDY4qAKy0wWJzwS42T7sVex 2p1AMK5wWN9JSz9/w0FvY7wQfT1aeOdLiQ== X-Google-Smtp-Source: ABdhPJyK4zmQa1TRe8iEuv3+b11YxvvdCn49FDRO+lSj4rvPzn/x2Dww8B4kujAsF7pg2TDxBlVjQw== X-Received: by 2002:a1c:ab8b:: with SMTP id u133mr3695642wme.108.1598025424347; Fri, 21 Aug 2020 08:57:04 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id l11sm5627385wme.11.2020.08.21.08.57.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Aug 2020 08:57:03 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 21 Aug 2020 16:56:39 +0100 Message-Id: <20200821155641.11839-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200821155641.11839-1-david.plowman@raspberrypi.com> References: <20200821155641.11839-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 3/5] 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 | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 236aa5c..a3f8438 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -324,6 +324,8 @@ public: uint32_t expectedSequence_; bool sensorMetadata_; + Transform transform_; + /* * 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,8 +402,27 @@ 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. + * Flipping the transpose bit in either input transform causes the + * corresponding bit in the combined result to flip. + */ + if (!!(combined & Transform::Transpose)) { + transform ^= Transform::Transpose; status = Adjusted; } @@ -610,6 +631,9 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) for (auto const stream : data->streams_) stream->reset(); + /* We will want to know the transform requested by the application. */ + data->transform_ = config->transform; + Size maxSize, sensorSize; unsigned int maxIndex = 0; bool rawStream = false; @@ -1174,8 +1198,10 @@ 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 rotation was already checked in RPiCameraConfiguration::validate. */ + Transform combined = transform_ * transformFromRotation(rotation); + ctrls.set(V4L2_CID_HFLIP, static_cast(!!(combined & Transform::HFlip))); + ctrls.set(V4L2_CID_VFLIP, static_cast(!!(combined & Transform::VFlip))); unicam_[Unicam::Image].dev()->setControls(&ctrls); } From patchwork Fri Aug 21 15:56:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9352 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 2CC69BE175 for ; Fri, 21 Aug 2020 15:57:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 906E96212A; Fri, 21 Aug 2020 17:57: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="MQ5ZW/+V"; dkim-atps=neutral Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E1E2361FE7 for ; Fri, 21 Aug 2020 17:57:05 +0200 (CEST) Received: by mail-wr1-x42a.google.com with SMTP id c15so2378105wrs.11 for ; Fri, 21 Aug 2020 08:57:05 -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=+zKAd8/wMfnxod5O3k1pUZg+x2G0KDNBQY8RY9TNoeo=; b=MQ5ZW/+VRNWqLAUne/S/yWw/D5X1OGh42vH3vuAiL0ZyrJ1dRQ4T1/gm/HBdMJtQa/ +Q6nhZ8oCSLg5vLkEsH5hanU2/WHWwmz4IZ/b4CNAxHD+FFaLYO1/90QBpQ/UKnCNqAm M2iSiqM5/mzVpTBnlXaVJ0+TQ8RMTXwlQkdHKO3Lu3ja6ae1x8wOaP0lrEU9wxWPvtWn yZlYCxA/pG/JFqSMctrwQa0tpSpcM1BHuF6tg2pmSECSU8+HZOFEI7i1VN2ty0tefH// Nr3zp38wRSPKtmU6lvdN8uAj/BTkLCQbczuEK6yxhzjKorz3tg/ZGgCWBxQqW0f/Yp7j r1Sw== 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=+zKAd8/wMfnxod5O3k1pUZg+x2G0KDNBQY8RY9TNoeo=; b=AuoYYOT/BntIj1nJGecNERe9ot1MBzadw3EmaaGlylWvoyOQpSmNaX1RUKh5d9OSn3 MqraYQ/g/Jhfd6R/MajO4K36jTYuFZiWF+trOE6zBGAgSjWGX2QgHTUSjde0XLsbca9j 44VaDEZEDQPugAnLFa1mMn/7ygeP0R9YledHgacIKUjsxGaWbtmjOciOwrAo8aUSEE+Q fT8H7LI4O+720cJQ4hVFBFQLWPfYWBP4pM3jr5zX5M1VPX1pgS8Ws0UvqUCRJJtHxFOI qjp84UisDfZf10Se3im2dewDyoEbMU6AhdhJp5MfFRoJs5bTtIuv1o015p6VGRkODN7a F1ZQ== X-Gm-Message-State: AOAM531zHYpFXx0LhNmkrBzsOdPfaElBYuZIxoOvHVTRmeMW7KzmXv5D dslRAnHx/HZN5KyQn9VJKAXdXUH1Y8srIA== X-Google-Smtp-Source: ABdhPJzArAe64T7jaCLnHKIpBDx00BBl8ATXvFC9QWoHiHIIjvJBjaMxAGCfdKQ9dz43KDymVErRVg== X-Received: by 2002:adf:bb14:: with SMTP id r20mr3266140wrg.366.1598025425192; Fri, 21 Aug 2020 08:57:05 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id l11sm5627385wme.11.2020.08.21.08.57.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Aug 2020 08:57:04 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 21 Aug 2020 16:56:40 +0100 Message-Id: <20200821155641.11839-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200821155641.11839-1-david.plowman@raspberrypi.com> References: <20200821155641.11839-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 4/5] 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 | 5 +- 3 files changed, 35 insertions(+), 22 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 3747208..0b36d57 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 a3f8438..4f9aa35 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -1159,6 +1159,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(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); @@ -1167,7 +1170,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 21 15:56:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9353 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 290AABE173 for ; Fri, 21 Aug 2020 15:57:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E7B70620AD; Fri, 21 Aug 2020 17:57:10 +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="Am6TeCs3"; dkim-atps=neutral Received: from mail-wr1-x441.google.com (mail-wr1-x441.google.com [IPv6:2a00:1450:4864:20::441]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2E3C562704 for ; Fri, 21 Aug 2020 17:57:07 +0200 (CEST) Received: by mail-wr1-x441.google.com with SMTP id f7so2432408wrw.1 for ; Fri, 21 Aug 2020 08:57:07 -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=CCMHN5/Ef9RcmUox9fxFCH7C4LVbiHbauYbkuYeFGYA=; b=Am6TeCs3+SEtS0qivg5ERPIfHQmpMAMJ6YKML21JitppBk6bT2dZP87e1oJyPp8dZ5 UNc7QOT5fpUjQuixXiFH0IGt6N2ymCtCOdcYR8qz1rc7m15IKyYXjbDWTKS/I6vLInzu xONi6RJTwcLnKjW0XQiz65du670PJyhs9gugVihqlLneshzKpWcw5sd7f6uadfdOHGKp 0vclNPQyUY6Q5tggzgCefpHAS8ObYJ6KvRqLBMYin/+AbO4pXOK7DVGuFMcxlLoffF3b O9R5/PmRsK7O5lkAHCzDNmHhTXhnp3ZWmJR8D6yCvwetWMaCPpMuLGHRNFa1ByTZRZp8 63GA== 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=CCMHN5/Ef9RcmUox9fxFCH7C4LVbiHbauYbkuYeFGYA=; b=XBCDbB6gBUjLNR7B5i6SGecD3p1fLFElybMKbkFN4UkYEApV8snNlOeWaXvylmAMBG uAQHca4yA0y6huLmmL7TzwMK5aX77oV+OXKNQA19S96qQPxiscdC43jz9n6sOIsbPvx6 t6PrfzhwhEXNH2QEnMZt6WiX5UWDQSsgUOlGzD26eLoib2i6lBCTeIvekzRdp43LI48i gJ8WLoXyYxYXCZfIXBPRFp3yWFBzktSGF+j0grNDtvXFs4PNuzrH7G9GCFeAp+8NWsmG ATN55rzgspX/f+IhYjvAl0rXhPbitsr2XBTd3DhobcTSmLSIbEvHsOQSmhptniNJK5Mv yZlQ== X-Gm-Message-State: AOAM531W5cinqef29otDbn1fcYAQ6Ivy/ORLI4BW0NnKLc3sT3NYiqtN 59rQqfzot7Cx4MklRGMSdauLCvNoSu6Vcg== X-Google-Smtp-Source: ABdhPJwaKFw8Gyc41gG8l8+BsGim/mx9GAqd1MFocns88emCOnv08ghE2Du6oPOCbPGCaXGWJ7r7Dg== X-Received: by 2002:a5d:4ccb:: with SMTP id c11mr3253864wrt.159.1598025426242; Fri, 21 Aug 2020 08:57:06 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id l11sm5627385wme.11.2020.08.21.08.57.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Aug 2020 08:57:05 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 21 Aug 2020 16:56:41 +0100 Message-Id: <20200821155641.11839-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200821155641.11839-1-david.plowman@raspberrypi.com> References: <20200821155641.11839-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 5/5] 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 9df713a..fdeda21 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 - @@ -429,6 +432,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 / @@ -441,6 +448,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++) {