From patchwork Sat Aug 29 11:54:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9427 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 24264BF019 for ; Sat, 29 Aug 2020 11:54:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 061AB6037D; Sat, 29 Aug 2020 13:54:37 +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="SY1SW3ml"; 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 638356037D for ; Sat, 29 Aug 2020 13:54:35 +0200 (CEST) Received: by mail-wm1-x332.google.com with SMTP id e17so1318228wme.0 for ; Sat, 29 Aug 2020 04:54:35 -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=SY1SW3mlx8xpe74+qtKRpZww4lc6z71d4JkA3ZJaPfqfMObR/8r8trmGeVTBorierZ 6YvPbVCheOhDmqYaI0iMd3rHTmbH5uoI5qrBk+hKJPfArcocGH9Agnu3kZzkVXqX5Ecm kzwYcU3WhqpRl+AJHXWCWlFSEbWxTvmYumxrycTK+87BBdipifTxJaNhEvaSBqPrRhSb kMFCss97pMgz3W2qmd9e9bRjFe9JJtBGFJwFpDJnAcRwSvWmyK2WzIFNc17M6ska2pn7 1neqhdqo2+kNp065Q3EgrBuMc65lxp7fBRi5NPrwKKttiyq6XQwyZGDA9ft/aFNGUa6H 4ihA== 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=DPgd62dtNIR5/nvBwWOzE5q57UySjq3LtD32gS2KXzsjcfYPQntZDGaMDqzoa73IvH W4km7CGd+suSbIZlkUqz+nU/lFy5wNgCDpvTT36kf5Y+bCTqz1H7LnSF/Tv9zWye+VDH bzEyQg/Tfxychxe9njXsNkLrs9ZbV4gPUgG4B+MSWYD852J8DYXJL5gMScAwJzceDkZ1 i7iPCCcmeo95dv4rpeunyZhbPHUGlNyJK37eyc5RuNvat2wlt2r5cfLh6G/dXeC/ApNI UiuhJFYPn7RS813mbBUt9DJ8BPaR0iHgsYflN5xZPUhqZMaKcqN30taNAowl+jnZJ+0T 1WiQ== X-Gm-Message-State: AOAM532Q4wCkwH5rCxJ344mgAbnJduEppLPONUPjKGQ2WfXguumdEUXK oyx9ArSWf6xWOpS+K+JtYmmW42RBVaWPRQ== X-Google-Smtp-Source: ABdhPJxJS/4y6RW3s5dn9aI8/QHjmbqI52qIMXjcrgI5fpO++9QtwdKbS4drU789+bNJP9AZrkSObA== X-Received: by 2002:a1c:c20a:: with SMTP id s10mr2945977wmf.55.1598702074887; Sat, 29 Aug 2020 04:54:34 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:34 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:22 +0100 Message-Id: <20200829115429.30010-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 1/8] 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: Laurent Pinchart --- 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 Sat Aug 29 11:54:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9428 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 EE7ABBF019 for ; Sat, 29 Aug 2020 11:54:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BB5F16292D; Sat, 29 Aug 2020 13:54:38 +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="nga6bylE"; dkim-atps=neutral Received: from mail-wr1-x442.google.com (mail-wr1-x442.google.com [IPv6:2a00:1450:4864:20::442]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4952262920 for ; Sat, 29 Aug 2020 13:54:36 +0200 (CEST) Received: by mail-wr1-x442.google.com with SMTP id f7so1579457wrw.1 for ; Sat, 29 Aug 2020 04:54:36 -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=+nJuZEg21MKi4Uhs6WPxSDSjqeKRFDsK98iClUN++28=; b=nga6bylEhon7ah3PjG7rIDg1Ogoz6v8snI9rzu9Y+30tEwUjK9LjN65dmM50TXoshi jPESaesPbpppo7TLM/bQLDfWSSr7YqVFnLpd0U1gDZ/gHS2G6/9Z3svRf3a5gIQAkB2W TTV0YDsRUifMHbbBrJ+vZigVA5CZfkd/IoGfU2uyOWMiLLqHEiwFuhIuBsXOvjUAr0vj ddhLtp5LBnDYaLtb5VHVEzb1G2mpLTe8pkhVhG/5O4Evuh/Rdc46FjoE9bPaMp8dUCuO sWGiwIgXZNM9AP3QFud+ZSxjVXi9ZHJT55922efUR6I4ZpPyh3yD2Vv4jJA60GwnTHus 3C5g== 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=+nJuZEg21MKi4Uhs6WPxSDSjqeKRFDsK98iClUN++28=; b=FASM7CWLDjnLPswUG6U4gLLMyJfTeIu0tanui/lSBhN8kLPQPKhJHrcJ9zBL/DJnFK +yaELf+eSh8RXXPUN4a9iCfMcXNL+aa5Wn4uBPsWl43xeZAEfTdUdMzsjTvxj/eZ7CgJ WjkCjyhCE2VHrZF8igjeouUzhudhAW5du6RMwrxMOuZtNDEmGrTAp8k2sF4tfAu2L6CM abjGwar/Pgpp/LvfcUL46HxNvh+ANUow0gL4fPjaOhBbg3GkD5lF5hX0JErAgUUcsGqU GGtdA0Vug/NF9d5RGpMcfAS+wsmvy2+TgioWSTMcFpGjd7yQNzIfLXaIaCeRwAIvg+Oj VnzQ== X-Gm-Message-State: AOAM533/Imu1PXu8rNMxvL3GiO5MwYzXsACVxLamUZMe5EkHfWYNNLoJ y1ncLgQlQ9tM25NOtvgIXRX3RT4bF+z5PQ== X-Google-Smtp-Source: ABdhPJzr1s+t1UyJD7al9OQHQYuR4bdoTOYOqhqBVnq/5/03Qn9xlbn3UedeX9j0hcLxlGtYn1WhYg== X-Received: by 2002:a5d:5052:: with SMTP id h18mr3387666wrt.156.1598702075651; Sat, 29 Aug 2020 04:54:35 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:35 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:23 +0100 Message-Id: <20200829115429.30010-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 2/8] libcamera: Allow access to v4l2_query_ext_ctrl structure for a V4L2 control 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 V4L2Device::queryCtrl method simply returns a pointer to the v4l2_query_ext_ctrl structure for the given control, which has already been retrieved and stored. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- include/libcamera/internal/v4l2_device.h | 2 ++ src/libcamera/v4l2_device.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h index 3b605aa..86dc05c 100644 --- a/include/libcamera/internal/v4l2_device.h +++ b/include/libcamera/internal/v4l2_device.h @@ -29,6 +29,8 @@ public: ControlList getControls(const std::vector &ids); int setControls(ControlList *ctrls); + const struct v4l2_query_ext_ctrl *queryControl(uint32_t id); + const std::string &deviceNode() const { return deviceNode_; } std::string devicePath() const; diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 65830d4..af48107 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -353,6 +353,21 @@ int V4L2Device::setControls(ControlList *ctrls) return ret; } +/** + * \brief Return the v4l2_query_ext_ctrl information for the given control. + * \param[in] id The id number of the V4L2 control. + * \return A pointer to the v4l2_query_ext_ctrl structure for the given + * control, or a null pointer if not found. + */ +const struct v4l2_query_ext_ctrl *V4L2Device::queryControl(uint32_t id) +{ + const auto it = controlInfo_.find(id); + if (it == controlInfo_.end()) + return nullptr; + + return &it->second; +} + /** * \brief Retrieve the device path in sysfs * From patchwork Sat Aug 29 11:54:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9429 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 918BBBF019 for ; Sat, 29 Aug 2020 11:54:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5F52C62933; Sat, 29 Aug 2020 13:54:41 +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="o/UdgCh8"; dkim-atps=neutral Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 650C262927 for ; Sat, 29 Aug 2020 13:54:37 +0200 (CEST) Received: by mail-wm1-x331.google.com with SMTP id v4so691191wmj.5 for ; Sat, 29 Aug 2020 04:54:37 -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=gnXbKjv6q7B0JIQAjcb2ETNBVm1ahEpsWC/SROR1KDA=; b=o/UdgCh8SAWr4lS4FLzs4ABSWyhu2vm9NXWuN6RnFWeotE6Hl2fIV3fbz2rX7Xmyl8 QaaGVhimBI8zqx6Q7CatpV3Q8wn3/gc567NomDHhjH1rv832lfuZOgi4H4HYKdHQyhk6 qpd0vtL94A0bVkJqCr82aUwpGxt4DqiVWQJbA8/zOzLlz9HUwfTInfPTq1hDOSBMC046 JNq3swA01LKmWRRCm80tk1o071SDUQy2BdbIkOy73SmgLE2rqU8wT/szCnjrJeRMwnIR GZAJmssm2FKmp1SoMy4UvmNjDGHRrEDhoJbkHzfZZ9l1TdKQLyLRqDyqBZ5vx9RrHXf6 vUHQ== 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=gnXbKjv6q7B0JIQAjcb2ETNBVm1ahEpsWC/SROR1KDA=; b=FZ6gs/Zn6xswTGM7FKwYUTp0FF7q3ey0iPo8FXqaOPdaOf221RKvvNwTH96sW1KYx9 j4jA1Mw7tliR4LgQ0L++aImXI1ewVwQCck5PEGWyMTdhC3WfslrvO2q2EPIBiu49kIc5 /SIIxFXryi8SNSuYDxPwj79U/oXp2joemtLs2RrT5URxAJMX26sFEkQVcVoBf7oStUgL XiCvOVgSKUzL+bgC3Nzw268iAwk5STmbJToXZpPjt29csAtPjcVGK2OJ9Q34+OTbho6Y ouodyzsgFHDE9pinirGZ0E5p8S2n+cYRkOIHYjcm3SWABvb/mElKrCo8IypX1glDac+r mYlg== X-Gm-Message-State: AOAM532DwutwO8jNVNiMtiQZfeVl/jRiJYVdZtr1EidMGMSWRHAvUDa8 K/2MxZdxq1QCAR4BreDwOaoizxyPKt/Kpw== X-Google-Smtp-Source: ABdhPJzrrlGaBjCj9SEkTfRAlnGV/bAtgC0F4ImFI82ml0FdzF8mly47BQkleQVK0L5MaQkmp0ovrQ== X-Received: by 2002:a1c:964b:: with SMTP id y72mr2956635wmd.69.1598702076575; Sat, 29 Aug 2020 04:54:36 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:36 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:24 +0100 Message-Id: <20200829115429.30010-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 3/8] libcamera: Add Transform enum to represent 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 Sat Aug 29 11:54:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9430 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 0029BBF019 for ; Sat, 29 Aug 2020 11:54:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BA7CC6037D; Sat, 29 Aug 2020 13:54:41 +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="lEWUsuT2"; dkim-atps=neutral Received: from mail-wm1-x344.google.com (mail-wm1-x344.google.com [IPv6:2a00:1450:4864:20::344]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DFA2162919 for ; Sat, 29 Aug 2020 13:54:37 +0200 (CEST) Received: by mail-wm1-x344.google.com with SMTP id z9so1426025wmk.1 for ; Sat, 29 Aug 2020 04:54:37 -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=WXNfmWM5mhgjldHNikuPTZpRraHHQgBPaE0+RxMTf0I=; b=lEWUsuT2lC4WeNyt/eFZFaX9iGCC5CIat9EJKbdE39CqxEfhf3ZaDJL7HfMEM6VCew WKN5CKCtfuKCBbCVZcLVmPLBlWPNeonqtAsWbgp8g6DaaAitQZ8Hk6iodvVc0C0izxI+ kG0jD1jxH3HAWxSjxQoledUg9HH0oyMfFPVF9vWpZqwtOiAW7wVHliiNq3JkQtzPfBhQ aUHgDOkZUKgKGvkThvRAuCnebyOGC3CVoOKVsMcyoa9huypLMLE4zmXQvnvPYdS/As+K XydQ82IyNQ+nK4mBj70FUuNazRiwUwMRfCD1PBJ1wMignCdu+iTZo3Eq0De8E2kFzvG1 4q1Q== 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=WXNfmWM5mhgjldHNikuPTZpRraHHQgBPaE0+RxMTf0I=; b=GiLDDZK6YkiychHBB2dZGyvhxjLkrmDz/PS9f78a7ElWCD4SVLF1thf5pUIMWsTFRq 4oNnJXSULKseasuNAeEFLkmyiNinkOng9p2EhLBwE4ufmr2ELN3rzo4r8bGfXvOZq8mS AhD5l4cVUjLOL7XbGLQoyp3xDS92ht//ALN5OdyHRVKLTnNYwR0VbW7yqbMO7Uoekzuh xcXMyKiXK42ny5PC6e0V84/vGYBElrzfsJpGv6tGrDz5R2AC+nk1bkH6dKWKM/DvY8cg LwCeYmK7n1dv9Z288Xk19Few6zqYkdOoCDocIG5FneyFYtF/i8agYx/HkOeUJr/0FF42 9fdw== X-Gm-Message-State: AOAM531V7TP9/dwcYhiqpwOi33dLuBKSTb7HDVyuU5SRa18hKtNgvOXs SlNF5s1HCgtb0ewftADIXCskORHyNL7V/g== X-Google-Smtp-Source: ABdhPJxwkpAJwcKchf+0C49KsdA9aV2AJzTybUaJQNjycccvcxU6BfjH3o0Heh7vlC2KivzGS+2Xqg== X-Received: by 2002:a7b:c5c1:: with SMTP id n1mr2792966wmk.125.1598702077315; Sat, 29 Aug 2020 04:54:37 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:36 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:25 +0100 Message-Id: <20200829115429.30010-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 4/8] libcamera: Allow Bayer pixel formats to be transformed 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 transform method to the V4L2PixelFormat class which allows Bayer formats to be re-ordered into a different Bayer order when horizontal or vertical flips are requested from the sensor. Signed-off-by: David Plowman --- include/libcamera/internal/v4l2_pixelformat.h | 3 + src/libcamera/v4l2_pixelformat.cpp | 94 ++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/v4l2_pixelformat.h b/include/libcamera/internal/v4l2_pixelformat.h index 9bfd81a..cddac86 100644 --- a/include/libcamera/internal/v4l2_pixelformat.h +++ b/include/libcamera/internal/v4l2_pixelformat.h @@ -14,6 +14,7 @@ #include #include +#include namespace libcamera { @@ -40,6 +41,8 @@ public: static V4L2PixelFormat fromPixelFormat(const PixelFormat &pixelFormat, bool multiplanar); + V4L2PixelFormat transform(Transform t) const; + private: uint32_t fourcc_; }; diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp index 30c94bb..28bcd51 100644 --- a/src/libcamera/v4l2_pixelformat.cpp +++ b/src/libcamera/v4l2_pixelformat.cpp @@ -101,6 +101,62 @@ const std::map vpf2pf{ { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), formats::MJPEG }, }; +/* Table giving the result of applying an hflip to each Bayer pixel format. */ +const std::map hflipTable{ + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16) }, +}; + +/* Table giving the result of applying a vflip to each Bayer pixel format. */ +const std::map vflipTable{ + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16) }, + { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16) }, +}; + } /* namespace */ /** @@ -113,7 +169,7 @@ const std::map vpf2pf{ /** * \fn V4L2PixelFormat::V4L2PixelFormat(uint32_t fourcc) - * \brief Construct a V4L2PixelFormat from a FourCC value +p * \brief Construct a V4L2PixelFormat from a FourCC value * \param[in] fourcc The pixel format FourCC numerical value */ @@ -205,4 +261,40 @@ V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat, return info.v4l2Format; } +/** + * \brief Transform a Bayer V4L2PixelFormat. + * \param[in] t The Transform to be applied to the pixel format. + * + * Some sensors that produce Bayer V4L2PixelFormats may change their Bayer + * order when the V4L2_CID_HFLIP or V4L2_CID_VFLIP controls are applied to the + * device. For such sensors, this function computes the new V4L2PixelFormat + * for the transformed Bayer order. + * + * Transposes are ignored as sensors do not implement them. + * + * \return The Bayer V4L2PixelFormat resulting from applying transform \a t + * to the given V4L2PixelFormat. + */ +V4L2PixelFormat V4L2PixelFormat::transform(Transform t) const +{ + V4L2PixelFormat result = *this; + + if (!!(t & Transform::HFlip)) { + const auto iter = hflipTable.find(*this); + if (iter == hflipTable.end()) { + LOG(V4L2, Warning) + << "Cannot transpose V4L2 pixel format " + << toString(); + return V4L2PixelFormat(); + } + result = iter->second; + } + + /* This will be found as the tables have the same keys. */ + if (!!(t & Transform::VFlip)) + result = vflipTable.find(result)->second; + + return result; +} + } /* namespace libcamera */ From patchwork Sat Aug 29 11:54:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9431 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 69B4DBF019 for ; Sat, 29 Aug 2020 11:54:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1AAA462927; Sat, 29 Aug 2020 13:54:42 +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="Xd1WGHgE"; dkim-atps=neutral Received: from mail-wm1-x330.google.com (mail-wm1-x330.google.com [IPv6:2a00:1450:4864:20::330]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B710362927 for ; Sat, 29 Aug 2020 13:54:38 +0200 (CEST) Received: by mail-wm1-x330.google.com with SMTP id s13so1446995wmh.4 for ; Sat, 29 Aug 2020 04:54:38 -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=LDActHwEwrBnVTFrJuJSA0jThMFh4nkAyDjeus8dcyw=; b=Xd1WGHgEUKSj5nONmY4DLmKmvl+XpTCGaJbVyRDTyYRak79YWCFKpPai993WZ5V6nQ Q1vHKP4x/gbI4eQefepAM/ZIbrpf3WVP0Iu74CFc6WeOPYTC4NBlHBnR1Bzj2phfxFNG wAOb0VD/iVOWCYU+uJ65p3DZUxalGo7lJnO+aSJ5uuLnV0rwHdiW7J9jj6LI/DuXkXJt aCa2F4SzAEBJUtETVSjwauaUvXU0YHOuvHJQiMdZd62bNbXEIDCg5G0euhX+j6d9Y08K boqXjJZI+ijvXlCC2og3meD99pMbbkND8HD7hejvHJ7BitXUDQBm2GyM+0phjPApwMKn 4XTg== 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=LDActHwEwrBnVTFrJuJSA0jThMFh4nkAyDjeus8dcyw=; b=EugVDjE00rhoFFMKF/DMQkpvMax04IfeA+4DOHiTqcmJCUcM3XIRg2yRd7oyMCKdIS QjbHZpox1FPjgPXkbq62m1mKNy8QpTs3QuqRhFzbLUNZeyYQUZIgRlVr8uhMYg9n6ci3 O+YMp9CDbog/wC4VDF+ke7K9vbZ7OgkRRUj6lVAVB1wSTnW3aU6cpSRruD5w3HTLehPt wvLiyLTjlhlo9raIGTDuvRZ1r+1r4r/OZuXh2Sk9SecUK0RpwwpnZzt0vBK+jwD8e7uw e5rEVP98aqxVyz34/LDHmbgVC5cmEDlHMmDle55M+QmZ1OHv7JBgG+9yiOJDD0FaZrz5 n8dw== X-Gm-Message-State: AOAM530RVZeKpb00/TIxA3cov6HhNI2mWW5/PYj/dik595M32IxzjWG3 wBlvaGLBeNnhbvsCKc0WSefn5ita4DGC/w== X-Google-Smtp-Source: ABdhPJwUESL0dXCZfxAdhgKCx3fyK87QmZQstGc7aWRIaonDkSWPfHimzFBaCLF+e8OKVPXtjmqUwA== X-Received: by 2002:a1c:27c2:: with SMTP id n185mr3103951wmn.78.1598702078134; Sat, 29 Aug 2020 04:54:38 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:37 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:26 +0100 Message-Id: <20200829115429.30010-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 5/8] 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 42c9caa..c554532 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 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 Sat Aug 29 11:54:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9432 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 2CE7CBF019 for ; Sat, 29 Aug 2020 11:54:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EDFC76293F; Sat, 29 Aug 2020 13:54:42 +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="l4pIK0dQ"; dkim-atps=neutral Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BD93C62920 for ; Sat, 29 Aug 2020 13:54:39 +0200 (CEST) Received: by mail-wr1-x42b.google.com with SMTP id b18so1552013wrs.7 for ; Sat, 29 Aug 2020 04:54:39 -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=zDr2YMtYWCg4u86sRPc68xnTPi105UzxUzv42mZx7uw=; b=l4pIK0dQ5OOYP6lifyWPIpOQCAr3S1cVqBxOvzhArCLB43ROPF1upMRR1uo0g8BY5N eQisYiExZpB0YlbNIezjxIDn6CK+4ZclNktb0YSSq9XsFooNjUDFTqlFBKEN9gkFVqTY cndBcfs0eJB3MVXGvhDgnxhbSpQ7vwUNsmeyJ1qPf6Zg34wnv2mj3esGxvoWPNIf04Yy B6GfkCK2m0c49o9VLGUNB5SAnZD0KdaNh0tuHdfKsHRyJrTG74sgwtJzgluQobGa41MW iM0SjO2Lt2Q9RgcMJGuL61+xAxFmRBpN9b2qxu2zNUptA2Fr1mxKlWOlN4JxDieB4Sz5 x39A== 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=zDr2YMtYWCg4u86sRPc68xnTPi105UzxUzv42mZx7uw=; b=cp0cJyJDpo9iXXTGKwMkGqwFcf4Z+6MG10g4gUTjSrXEicXA7dvNevXvsoFiqF7ySl quncB3bpDyJFqwRzuJuFHa3JFfOPrt2cZxu7CTztFtJN9UsN+7wvuHzHluZLSnBvQvut wC8/TFGezBxFsAKYWqW9xVImcFVL2sRRssmubRn90Ki6R1TItZyA+ZRNirugYixZ6cKF yTWpGpRfp8hYywklzM6X09Y4qSkml3rxmcK6PtlTFkZP2OEAs0yMy6voixwQMIXqj0DD KEDvuYsXGSf05GwwScrxLM8R23D/snOjNoqk5gQeuJozfCEBZlgSvswgRqMzlk8jRgBr a7kg== X-Gm-Message-State: AOAM530OrenagWmg7XfEIxdvOtb5Pk92HSV3hymzMibtBmVS8VrM0q1c Sdhwfido32GTFu5TE4n7Q6rrPUgZ1zfk/g== X-Google-Smtp-Source: ABdhPJweY8ywZbVOribAyDsMtQj5rjI/4HeHkM5MREYB3GHV1SaQ26jaEHOc8ugrLicVItcpo1EuVA== X-Received: by 2002:adf:ef0a:: with SMTP id e10mr1414775wro.362.1598702079089; Sat, 29 Aug 2020 04:54:39 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:38 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:27 +0100 Message-Id: <20200829115429.30010-7-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 6/8] 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. Note that the validate() method has to work out what the final Bayer order of any raw streams will be, before configure() actually applies the transform to the sensor. Signed-off-by: David Plowman --- .../pipeline/raspberrypi/raspberrypi.cpp | 82 ++++++++++++++++--- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index c554532..333aa94 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); @@ -400,8 +400,34 @@ 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; } @@ -414,13 +440,42 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() * Calculate the best sensor mode we can use based on * the user request. */ - V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats(); + V4L2VideoDevice *dev = data_->unicam_[Unicam::Image].dev(); + V4L2VideoDevice::Formats fmts = dev->formats(); V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size); - int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat); + int ret = dev->tryFormat(&sensorFormat); if (ret) return Invalid; - PixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat(); + /* + * Some sensors change their Bayer order when they are + * h-flipped or v-flipped, according to the transform. + * If the controls own up to "modifying the layout" we + * will assume that's what is going on and advertise + * the transformed Bayer order in the stream. + */ + V4L2PixelFormat fourcc = sensorFormat.fourcc; + + /* + * The camera may already be transformed, so we must + * only transform the fourcc if the new transform is + * different. + */ + ControlList ctrls = dev->getControls({ V4L2_CID_HFLIP, V4L2_CID_VFLIP }); + + const struct v4l2_query_ext_ctrl *hflipCtrl = dev->queryControl(V4L2_CID_HFLIP); + if (hflipCtrl && + (hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) && + ctrls.get(V4L2_CID_HFLIP).get() != !!(combined & Transform::HFlip)) + fourcc = fourcc.transform(Transform::HFlip); + + const struct v4l2_query_ext_ctrl *vflipCtrl = dev->queryControl(V4L2_CID_VFLIP); + if (vflipCtrl && + (vflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) && + ctrls.get(V4L2_CID_VFLIP).get() != !!(combined & Transform::VFlip)) + fourcc = fourcc.transform(Transform::VFlip); + + PixelFormat sensorPixFormat = fourcc.toPixelFormat(); if (cfg.size != sensorFormat.size || cfg.pixelFormat != sensorPixFormat) { cfg.size = sensorFormat.size; @@ -756,7 +811,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; @@ -1112,7 +1167,7 @@ int RPiCameraData::loadIPA() return ipa_->init(settings); } -int RPiCameraData::configureIPA() +int RPiCameraData::configureIPA(CameraConfiguration *config) { std::map streamConfig; std::map entityControls; @@ -1170,11 +1225,16 @@ int RPiCameraData::configureIPA() sensorMetadata_ = result.data[2]; } - /* Configure the H/V flip controls based on the sensor rotation. */ + /* + * Configure the H/V flip controls based on the sensor rotation + * and user transform. + */ 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 angle was already checked in validate(). */ + Transform combined = config->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 Sat Aug 29 11:54:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9433 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 C13EDBF019 for ; Sat, 29 Aug 2020 11:54:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7C3C462920; Sat, 29 Aug 2020 13:54:43 +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="FojP9stv"; dkim-atps=neutral Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9BE1D628D4 for ; Sat, 29 Aug 2020 13:54:40 +0200 (CEST) Received: by mail-wr1-x444.google.com with SMTP id i7so1525104wre.13 for ; Sat, 29 Aug 2020 04:54:40 -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=1wzO3ADHuXaXMNcEGpjIWdSK22Nw7YClFTicVLSxRIc=; b=FojP9stvs/WnXhtJRNuJJ/8wpZVHaryIHdEPWVtUYryCWnJTIaPMwZpfOUSQS536cQ bCon9rUHsV9OX1wfvhv8vXgtPeZ1T3HJhCk4cx477seMZyKMGfXVAlncoX6Xi+DJUp13 OV42hx5YjHoIakHIJ5Vm5qvtSp0S1QJBDbg/z87PWrko5t0v54lW1asTbO9Do4tqygg2 87tBnF64zbRzVhgELr0+WC3W7MMpFiMuTLT3ET7KrPfIEM8ww+8AbbBsagCHPEr9Y2pM bHuw5Iz9/KUwROf4vQZjIleONzgXQ1ECbmUX2CoAJA3tDbKd76jhpo1pvFc23j/f9Qxm 19RA== 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=1wzO3ADHuXaXMNcEGpjIWdSK22Nw7YClFTicVLSxRIc=; b=qpazl/c2AvH1oAhgwxLwu0cfZQMnPsn2e+eEGOoJlkuS5k2niDPr4bNRO8m8rYdN4j 2uHFoNbprUlCZw50oYOuIUm4qxvC3KYMkaiPoM3okNIjpFsWVhPClwFl9i+irMCFx1Up PMWwlxD9eo/MLM4yByMmFaF8evFJG5sWUpvqUZcVqpN3I+a/pITKml4CPNgvCW92/k0z j6qT/RblpiJ5OwJzH53w+lKfmPATAMXPepn4ifU53bVCR73/K8GeLvklkjaNQbvUfR95 jaYg37Qx07U2U7Y2JDmvivp2RPCTnw4hb4RhMeXjr4FTcmBxh8NF9fCtsfTPrL0wfcJh oQvQ== X-Gm-Message-State: AOAM530Mj6dkEL58y4US2splO1g0dqc1pYQDqOEdSq9N9MS9aIJXAgX2 YAlHKCdUhrEozoKf7ZGsrjkjOmrJCTMbWA== X-Google-Smtp-Source: ABdhPJxcT9PZ9m7fYRMQKU7/2llyHRjTUkjt7izFWQmIN3PjPc6YI4p7xK0tbzf7/XYI0C1hQa+yCA== X-Received: by 2002:adf:e5c8:: with SMTP id a8mr3319935wrn.5.1598702080010; Sat, 29 Aug 2020 04:54:40 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:39 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:28 +0100 Message-Id: <20200829115429.30010-8-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 7/8] 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 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 333aa94..07afd58 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -1189,6 +1189,9 @@ int RPiCameraData::configureIPA(CameraConfiguration *config) 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); @@ -1197,7 +1200,7 @@ int RPiCameraData::configureIPA(CameraConfiguration *config) /* 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 Sat Aug 29 11:54:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9434 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 650E8BF019 for ; Sat, 29 Aug 2020 11:54:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2DE1662949; Sat, 29 Aug 2020 13:54:45 +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="Uc2eS+Zh"; dkim-atps=neutral Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 94D466293D for ; Sat, 29 Aug 2020 13:54:41 +0200 (CEST) Received: by mail-wr1-x443.google.com with SMTP id w5so1545533wrp.8 for ; Sat, 29 Aug 2020 04:54:41 -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=Uc2eS+ZhSzmHaCXNo5noUxlX+hYu0qMKw7cPflvDH2sRPBhFGcLi4334Qxt6ChNE9y GRxJZaS4dXnpUMSxlvD999iQOOqaVEUUo2H1Qk3bdFX/MJKiu5ryQ4el1Y13xJ7CrIt9 /K0iiG5XI0ryZt2tloDpE/oN8Wq1n36XjZxcSA2OuH/ytQSoo2os1OgKnzTeitOWZVJC E5dMAGFcF6x56wSvwGupqeA9qpL+duP5jK2tyx5Uh9G+Q04u6LkwrARl0COxjXpUYC5n KB7Iiw5+xWgYoRbHZdF5KAT7h8tMQUBeg0Mza7Wt6xkH3KPXdDMcbldh+psEL/5syqVq slNA== 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=jB3DmbFU8UsIBICoDo7XnJYCZsDn1dU/Cu0YC6pMH9Yg7+IXw3glbwn4wUmKXC7DQT rq2XBQY6GUc0hEf4SiKYkD1W1md8AnTchhjmiZtDJ0RLJ7Hqlpc1JP3zjYt+NUyatFp+ UGj+bvrjTfyBYmsO7lD7XEJsTMgV5PX/AmWIHl+KpzSM17e0jY/15/JgL957Mk3q/Dqe J/mRm1DCzBFglU5yQgNuXEiWH9sXIOqYI1dgYO+8hInZsZ1F3YIkF/XVqF4BPkD06T5y tMQ4VQahJWYS9AnSHr/PmMDHlgaY0Jy+KHdDCnY06RS8dO2nqstFbus9tsOnJlF5Lcit ZvZg== X-Gm-Message-State: AOAM531Rm48ojDH5eSDjrrh4RxMgCCU8SD0oFRYJmGGrmUpFPFBLv743 s+fyz8vT2H+qo0pDggJGp19qRV9n5pOQiA== X-Google-Smtp-Source: ABdhPJwKKElD/Gy7KFniGN8PUW+FAbFYeS8r+8DWQv4tO4pzn+3SZ3bxgSt8kdGNUx2p4+bQGjDpWQ== X-Received: by 2002:adf:a48d:: with SMTP id g13mr3227683wrb.212.1598702081052; Sat, 29 Aug 2020 04:54:41 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v16sm3071523wmj.14.2020.08.29.04.54.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Aug 2020 04:54:40 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Sat, 29 Aug 2020 12:54:29 +0100 Message-Id: <20200829115429.30010-9-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200829115429.30010-1-david.plowman@raspberrypi.com> References: <20200829115429.30010-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 8/8] 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++) {