chore(users/flokli/ipu6-softisp): refresh libcamera patches
Refresh them with the patches from https://patchwork.libcamera.org/cover/19663/. This is still based off v0.2.0. Change-Id: I875fd64e3bb71a95c92af1108a23d27c0f3494e0 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11179 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de> Autosubmit: flokli <flokli@flokli.de>
This commit is contained in:
parent
622efa86fa
commit
9948eb64d1
33 changed files with 3861 additions and 3655 deletions
|
@ -3,39 +3,36 @@
|
|||
let
|
||||
libcamera = pkgs.libcamera.overrideAttrs (old: {
|
||||
mesonFlags = old.mesonFlags or [ ] ++ [
|
||||
"-Dpipelines=simple/simple,ipu3,uvcvideo"
|
||||
"-Dipas=simple/simple,ipu3"
|
||||
"-Dpipelines=simple,ipu3,uvcvideo"
|
||||
"-Dipas=simple,ipu3"
|
||||
];
|
||||
|
||||
# This is
|
||||
# https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/libcamera.git/plain/libcamera-0.2.0-softisp.patch?h=f39&id=60e6b3d5e366a360a75942073dc0d642e4900982,
|
||||
# but manually piped to git and back, as some renames were not processed properly.
|
||||
# It was later refreshed with https://patchwork.libcamera.org/cover/19663/
|
||||
patches = old.patches or [ ] ++ [
|
||||
./libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
|
||||
./libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
|
||||
./libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
|
||||
./libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
|
||||
./libcamera/0005-libcamera-internal-Document-the-SharedMemObject-clas.patch
|
||||
./libcamera/0006-libcamera-introduce-SoftwareIsp-class.patch
|
||||
./libcamera/0007-libcamera-software_isp-Add-SwStats-base-class.patch
|
||||
./libcamera/0008-libcamera-software_isp-Add-SwStatsCpu-class.patch
|
||||
./libcamera/0009-libcamera-software_isp-Add-Debayer-base-class.patch
|
||||
./libcamera/0010-libcamera-software_isp-Add-DebayerCpu-class.patch
|
||||
./libcamera/0011-libcamera-ipa-add-Soft-IPA-common-files.patch
|
||||
./libcamera/0012-libcamera-ipa-Soft-IPA-add-a-Simple-Soft-IPA-impleme.patch
|
||||
./libcamera/0013-libcamera-software_isp-add-Simple-SoftwareIsp-implem.patch
|
||||
./libcamera/0014-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
|
||||
./libcamera/0015-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
|
||||
./libcamera/0016-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
|
||||
./libcamera/0017-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
|
||||
./libcamera/0018-libcamera-debayer_cpu-Add-BGR888-output-support.patch
|
||||
./libcamera/0019-libcamera-pipeline-simple-Enable-simplepipeline-for-.patch
|
||||
./libcamera/0020-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
|
||||
./libcamera/0021-libcamera-swstats_cpu-Add-support-for-10bpp-IGIG_GBG.patch
|
||||
./libcamera/0022-libcamera-debayer_cpu-Add-support-for-10bpp-IGIG_GBG.patch
|
||||
./libcamera/0023-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
|
||||
./libcamera/0024-ov01a1s-HACK.patch
|
||||
./libcamera/0025-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
|
||||
./libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
|
||||
./libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
|
||||
./libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
|
||||
./libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
|
||||
./libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
|
||||
./libcamera/0010-libcamera-introduce-SoftwareIsp.patch
|
||||
./libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
|
||||
./libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
|
||||
./libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
|
||||
./libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
|
||||
./libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
|
||||
./libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
|
||||
./libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
|
||||
./libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
|
||||
./libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
|
||||
./libcamera/0020-ov01a1s-HACK.patch
|
||||
./libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
@ -1,34 +1,73 @@
|
|||
From aa818f7b749122f916be1ced48d1a3a2b3aeb47e Mon Sep 17 00:00:00 2001
|
||||
From d86746fc1739f678e4bafe43f5047cba9b6b053e Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Tue, 2 Jan 2024 23:47:20 +0300
|
||||
Subject: [PATCH 01/25] libcamera: pipeline: simple: fix size adjustment in
|
||||
Date: Mon, 11 Mar 2024 15:15:05 +0100
|
||||
Subject: [PATCH 01/21] libcamera: pipeline: simple: fix size adjustment in
|
||||
validate()
|
||||
|
||||
SimpleCameraConfiguration::validate() adjusts the configuration
|
||||
of its streams (if the size is not in the outputSizes) to
|
||||
the captureSize. But the captureSize itself can be not in the
|
||||
outputSizes, and then the adjusted configuration won't be
|
||||
valid resulting in camera configuration failure.
|
||||
SimpleCameraConfiguration::validate() adjusts the configuration of its
|
||||
streams (if the size is not in the outputSizes) to the captureSize. But
|
||||
the captureSize itself can be not in the outputSizes, and then the
|
||||
adjusted configuration won't be valid resulting in camera configuration
|
||||
failure.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/pipeline/simple/simple.cpp | 7 +++++--
|
||||
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||
src/libcamera/pipeline/simple/simple.cpp | 37 ++++++++++++++++++++++--
|
||||
1 file changed, 35 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
|
||||
index 911051b2..4d0e7255 100644
|
||||
index 911051b2..a84f6760 100644
|
||||
--- a/src/libcamera/pipeline/simple/simple.cpp
|
||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
|
||||
@@ -997,10 +997,13 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
|
||||
@@ -881,6 +881,30 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
|
||||
{
|
||||
}
|
||||
|
||||
+namespace {
|
||||
+
|
||||
+static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
|
||||
+{
|
||||
+ ASSERT(supportedSizes.min <= supportedSizes.max);
|
||||
+
|
||||
+ if (supportedSizes.min == supportedSizes.max)
|
||||
+ return supportedSizes.max;
|
||||
+
|
||||
+ unsigned int hStep = supportedSizes.hStep;
|
||||
+ unsigned int vStep = supportedSizes.vStep;
|
||||
+
|
||||
+ if (hStep == 0)
|
||||
+ hStep = supportedSizes.max.width - supportedSizes.min.width;
|
||||
+ if (vStep == 0)
|
||||
+ vStep = supportedSizes.max.height - supportedSizes.min.height;
|
||||
+
|
||||
+ Size adjusted = requestedSize.boundedTo(supportedSizes.max).expandedTo(supportedSizes.min);
|
||||
+
|
||||
+ return adjusted.shrunkBy(supportedSizes.min).alignedDownTo(hStep, vStep).grownBy(supportedSizes.min);
|
||||
+}
|
||||
+
|
||||
+} /* namespace */
|
||||
+
|
||||
CameraConfiguration::Status SimpleCameraConfiguration::validate()
|
||||
{
|
||||
const CameraSensor *sensor = data_->sensor_.get();
|
||||
@@ -997,10 +1021,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
|
||||
}
|
||||
|
||||
if (!pipeConfig_->outputSizes.contains(cfg.size)) {
|
||||
+ Size adjustedSize = pipeConfig_->captureSize;
|
||||
+ /*
|
||||
+ * The converter (when present) may not be able to output
|
||||
+ * a size identical to its input size. The capture size is thus
|
||||
+ * not guaranteed to be a valid output size. In such cases, use
|
||||
+ * the smaller valid output size closest to the requested.
|
||||
+ */
|
||||
+ if (!pipeConfig_->outputSizes.contains(adjustedSize))
|
||||
+ adjustedSize = pipeConfig_->outputSizes.max;
|
||||
+ adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes);
|
||||
LOG(SimplePipeline, Debug)
|
||||
<< "Adjusting size from " << cfg.size
|
||||
- << " to " << pipeConfig_->captureSize;
|
||||
|
@ -39,5 +78,5 @@ index 911051b2..4d0e7255 100644
|
|||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
From ca3bcfde49f069a85f7860f61d8c3bd196f97139 Mon Sep 17 00:00:00 2001
|
||||
From 96e50c6a43352a9cb81d558fea27e580f2b26585 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Tue, 26 Dec 2023 16:55:08 +0300
|
||||
Subject: [PATCH 02/25] libcamera: internal: Move dma_heaps.[h,cpp] to common
|
||||
Date: Mon, 11 Mar 2024 15:15:06 +0100
|
||||
Subject: [PATCH 02/21] libcamera: internal: Move dma_heaps.[h, cpp] to common
|
||||
directories
|
||||
|
||||
DmaHeap class is useful outside the RPi pipeline handler too.
|
||||
|
@ -9,20 +9,26 @@ DmaHeap class is useful outside the RPi pipeline handler too.
|
|||
Move dma_heaps.h and dma_heaps.cpp to common directories. Update
|
||||
the build files and RPi vc4 pipeline handler accordingly.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
|
||||
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
---
|
||||
.../libcamera/internal}/dma_heaps.h | 4 ----
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
.../{pipeline/rpi/vc4 => }/dma_heaps.cpp | 18 +++++++-----------
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/pipeline/rpi/vc4/meson.build | 1 -
|
||||
src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 ++---
|
||||
6 files changed, 11 insertions(+), 19 deletions(-)
|
||||
.../libcamera/internal}/dma_heaps.h | 4 -
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
src/libcamera/dma_heaps.cpp | 127 ++++++++++++++++++
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp | 90 -------------
|
||||
src/libcamera/pipeline/rpi/vc4/meson.build | 1 -
|
||||
src/libcamera/pipeline/rpi/vc4/vc4.cpp | 5 +-
|
||||
7 files changed, 131 insertions(+), 98 deletions(-)
|
||||
rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%)
|
||||
rename src/libcamera/{pipeline/rpi/vc4 => }/dma_heaps.cpp (83%)
|
||||
create mode 100644 src/libcamera/dma_heaps.cpp
|
||||
delete mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
|
||||
|
||||
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
|
||||
similarity index 92%
|
||||
|
@ -59,85 +65,139 @@ index 7f1f3440..33eb0fb3 100644
|
|||
'formats.h',
|
||||
'framebuffer.h',
|
||||
'ipa_manager.h',
|
||||
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
|
||||
similarity index 83%
|
||||
rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
|
||||
rename to src/libcamera/dma_heaps.cpp
|
||||
index 317b1fc1..7444d9c2 100644
|
||||
--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
|
||||
diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
|
||||
new file mode 100644
|
||||
index 00000000..38ef175a
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/dma_heaps.cpp
|
||||
@@ -5,8 +5,6 @@
|
||||
* dma_heaps.h - Helper class for dma-heap allocations.
|
||||
*/
|
||||
|
||||
-#include "dma_heaps.h"
|
||||
-
|
||||
#include <array>
|
||||
#include <fcntl.h>
|
||||
#include <linux/dma-buf.h>
|
||||
@@ -16,6 +14,8 @@
|
||||
|
||||
#include <libcamera/base/log.h>
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2020, Raspberry Pi Ltd
|
||||
+ *
|
||||
+ * dma_heaps.h - Helper class for dma-heap allocations.
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/dma_heaps.h"
|
||||
+
|
||||
/*
|
||||
* /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
|
||||
* to only have to worry about importing.
|
||||
@@ -30,9 +30,7 @@ static constexpr std::array<const char *, 2> heapNames = {
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
-LOG_DECLARE_CATEGORY(RPI)
|
||||
-
|
||||
-namespace RPi {
|
||||
+#include <array>
|
||||
+#include <fcntl.h>
|
||||
+#include <sys/ioctl.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include <linux/dma-buf.h>
|
||||
+#include <linux/dma-heap.h>
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+/**
|
||||
+ * \file dma_heaps.cpp
|
||||
+ * \brief CMA dma-heap allocator
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
|
||||
+ * to only have to worry about importing.
|
||||
+ *
|
||||
+ * Annoyingly, should the cma heap size be specified on the kernel command line
|
||||
+ * instead of DT, the heap gets named "reserved" instead.
|
||||
+ */
|
||||
+static constexpr std::array<const char *, 2> heapNames = {
|
||||
+ "/dev/dma_heap/linux,cma",
|
||||
+ "/dev/dma_heap/reserved"
|
||||
+};
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(DmaHeap)
|
||||
|
||||
DmaHeap::DmaHeap()
|
||||
{
|
||||
@@ -40,7 +38,7 @@ DmaHeap::DmaHeap()
|
||||
int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
- LOG(RPI, Debug) << "Failed to open " << name << ": "
|
||||
+ LOG(DmaHeap, Debug) << "Failed to open " << name << ": "
|
||||
<< strerror(ret);
|
||||
continue;
|
||||
}
|
||||
@@ -50,7 +48,7 @@ DmaHeap::DmaHeap()
|
||||
}
|
||||
|
||||
if (!dmaHeapHandle_.isValid())
|
||||
- LOG(RPI, Error) << "Could not open any dmaHeap device";
|
||||
+
|
||||
+/**
|
||||
+ * \class DmaHeap
|
||||
+ * \brief Helper class for CMA dma-heap allocations
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
|
||||
+ *
|
||||
+ * Goes through the internal list of possible names of the CMA dma-heap devices
|
||||
+ * until a CMA dma-heap device is successfully opened. If it fails to open any
|
||||
+ * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
|
||||
+ * object owns a wrapped dma-heap file descriptor.
|
||||
+ *
|
||||
+ * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
|
||||
+ */
|
||||
+DmaHeap::DmaHeap()
|
||||
+{
|
||||
+ for (const char *name : heapNames) {
|
||||
+ int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
|
||||
+ if (ret < 0) {
|
||||
+ ret = errno;
|
||||
+ LOG(DmaHeap, Debug)
|
||||
+ << "Failed to open " << name << ": "
|
||||
+ << strerror(ret);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ dmaHeapHandle_ = UniqueFD(ret);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!dmaHeapHandle_.isValid())
|
||||
+ LOG(DmaHeap, Error) << "Could not open any dmaHeap device";
|
||||
}
|
||||
|
||||
DmaHeap::~DmaHeap() = default;
|
||||
@@ -69,7 +67,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
|
||||
|
||||
ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
|
||||
if (ret < 0) {
|
||||
- LOG(RPI, Error) << "dmaHeap allocation failure for "
|
||||
+ LOG(DmaHeap, Error) << "dmaHeap allocation failure for "
|
||||
<< name;
|
||||
return {};
|
||||
}
|
||||
@@ -77,7 +75,7 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
|
||||
UniqueFD allocFd(alloc.fd);
|
||||
ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
|
||||
if (ret < 0) {
|
||||
- LOG(RPI, Error) << "dmaHeap naming failure for "
|
||||
+ LOG(DmaHeap, Error) << "dmaHeap naming failure for "
|
||||
<< name;
|
||||
return {};
|
||||
}
|
||||
@@ -85,6 +83,4 @@ UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
|
||||
return allocFd;
|
||||
}
|
||||
|
||||
-} /* namespace RPi */
|
||||
-
|
||||
} /* namespace libcamera */
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Destroy the DmaHeap instance
|
||||
+ *
|
||||
+ * Destroying a DmaHeap instance which owns a wrapped dma-heap file descriptor
|
||||
+ * closes the descriptor automatically.
|
||||
+ */
|
||||
+DmaHeap::~DmaHeap() = default;
|
||||
+
|
||||
+/**
|
||||
+ * \fn DmaHeap::isValid()
|
||||
+ * \brief Check if the DmaHeap instance is valid
|
||||
+ * \return True if the DmaHeap is valid, false otherwise
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Allocate a dma-buf from the DmaHeap
|
||||
+ * \param [in] name The name to set for the allocated buffer
|
||||
+ * \param [in] size The size of the buffer to allocate
|
||||
+ * \return The \ref UniqueFD of the allocated buffer
|
||||
+ *
|
||||
+ * Allocates a dma-buf with read/write access.
|
||||
+ * If the allocation fails returns invalid UniqueFD.
|
||||
+ */
|
||||
+UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!name)
|
||||
+ return {};
|
||||
+
|
||||
+ struct dma_heap_allocation_data alloc = {};
|
||||
+
|
||||
+ alloc.len = size;
|
||||
+ alloc.fd_flags = O_CLOEXEC | O_RDWR;
|
||||
+
|
||||
+ ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
|
||||
+ if (ret < 0) {
|
||||
+ LOG(DmaHeap, Error) << "dmaHeap allocation failure for " << name;
|
||||
+ return {};
|
||||
+ }
|
||||
+
|
||||
+ UniqueFD allocFd(alloc.fd);
|
||||
+ ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
|
||||
+ if (ret < 0) {
|
||||
+ LOG(DmaHeap, Error) << "dmaHeap naming failure for " << name;
|
||||
+ return {};
|
||||
+ }
|
||||
+
|
||||
+ return allocFd;
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
|
||||
index 45f63e93..3c5e43df 100644
|
||||
--- a/src/libcamera/meson.build
|
||||
|
@ -150,6 +210,102 @@ index 45f63e93..3c5e43df 100644
|
|||
'fence.cpp',
|
||||
'formats.cpp',
|
||||
'framebuffer.cpp',
|
||||
diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
|
||||
deleted file mode 100644
|
||||
index 317b1fc1..00000000
|
||||
--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
|
||||
+++ /dev/null
|
||||
@@ -1,90 +0,0 @@
|
||||
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
-/*
|
||||
- * Copyright (C) 2020, Raspberry Pi Ltd
|
||||
- *
|
||||
- * dma_heaps.h - Helper class for dma-heap allocations.
|
||||
- */
|
||||
-
|
||||
-#include "dma_heaps.h"
|
||||
-
|
||||
-#include <array>
|
||||
-#include <fcntl.h>
|
||||
-#include <linux/dma-buf.h>
|
||||
-#include <linux/dma-heap.h>
|
||||
-#include <sys/ioctl.h>
|
||||
-#include <unistd.h>
|
||||
-
|
||||
-#include <libcamera/base/log.h>
|
||||
-
|
||||
-/*
|
||||
- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
|
||||
- * to only have to worry about importing.
|
||||
- *
|
||||
- * Annoyingly, should the cma heap size be specified on the kernel command line
|
||||
- * instead of DT, the heap gets named "reserved" instead.
|
||||
- */
|
||||
-static constexpr std::array<const char *, 2> heapNames = {
|
||||
- "/dev/dma_heap/linux,cma",
|
||||
- "/dev/dma_heap/reserved"
|
||||
-};
|
||||
-
|
||||
-namespace libcamera {
|
||||
-
|
||||
-LOG_DECLARE_CATEGORY(RPI)
|
||||
-
|
||||
-namespace RPi {
|
||||
-
|
||||
-DmaHeap::DmaHeap()
|
||||
-{
|
||||
- for (const char *name : heapNames) {
|
||||
- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
|
||||
- if (ret < 0) {
|
||||
- ret = errno;
|
||||
- LOG(RPI, Debug) << "Failed to open " << name << ": "
|
||||
- << strerror(ret);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- dmaHeapHandle_ = UniqueFD(ret);
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- if (!dmaHeapHandle_.isValid())
|
||||
- LOG(RPI, Error) << "Could not open any dmaHeap device";
|
||||
-}
|
||||
-
|
||||
-DmaHeap::~DmaHeap() = default;
|
||||
-
|
||||
-UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
|
||||
-{
|
||||
- int ret;
|
||||
-
|
||||
- if (!name)
|
||||
- return {};
|
||||
-
|
||||
- struct dma_heap_allocation_data alloc = {};
|
||||
-
|
||||
- alloc.len = size;
|
||||
- alloc.fd_flags = O_CLOEXEC | O_RDWR;
|
||||
-
|
||||
- ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
|
||||
- if (ret < 0) {
|
||||
- LOG(RPI, Error) << "dmaHeap allocation failure for "
|
||||
- << name;
|
||||
- return {};
|
||||
- }
|
||||
-
|
||||
- UniqueFD allocFd(alloc.fd);
|
||||
- ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
|
||||
- if (ret < 0) {
|
||||
- LOG(RPI, Error) << "dmaHeap naming failure for "
|
||||
- << name;
|
||||
- return {};
|
||||
- }
|
||||
-
|
||||
- return allocFd;
|
||||
-}
|
||||
-
|
||||
-} /* namespace RPi */
|
||||
-
|
||||
-} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build
|
||||
index cdb049c5..386e2296 100644
|
||||
--- a/src/libcamera/pipeline/rpi/vc4/meson.build
|
||||
|
@ -190,5 +346,5 @@ index 26102ea7..3a42e75e 100644
|
|||
|
||||
struct Config {
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
From 6d5f3b0b54df4ff66079675a4c1f0f0b76778e22 Mon Sep 17 00:00:00 2001
|
||||
From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Wed, 10 Jan 2024 23:51:25 +0300
|
||||
Subject: [PATCH 03/25] libcamera: dma_heaps: extend DmaHeap class to support
|
||||
Date: Mon, 11 Mar 2024 15:15:07 +0100
|
||||
Subject: [PATCH 03/21] libcamera: dma_heaps: extend DmaHeap class to support
|
||||
system heap
|
||||
|
||||
Add an argument to the constructor to specify dma heaps type(s)
|
||||
|
@ -9,17 +9,19 @@ to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System.
|
|||
By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and
|
||||
DmaHeapFlag::System are set, CMA heap is tried first.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
include/libcamera/internal/dma_heaps.h | 12 +++++++-
|
||||
src/libcamera/dma_heaps.cpp | 39 +++++++++++++++-----------
|
||||
2 files changed, 34 insertions(+), 17 deletions(-)
|
||||
include/libcamera/internal/dma_heaps.h | 12 ++++-
|
||||
src/libcamera/dma_heaps.cpp | 67 ++++++++++++++++++++------
|
||||
2 files changed, 63 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
|
||||
index cff8f140..22aa1007 100644
|
||||
index cff8f140..80bf29e7 100644
|
||||
--- a/include/libcamera/internal/dma_heaps.h
|
||||
+++ b/include/libcamera/internal/dma_heaps.h
|
||||
@@ -9,6 +9,7 @@
|
||||
|
@ -36,8 +38,8 @@ index cff8f140..22aa1007 100644
|
|||
public:
|
||||
- DmaHeap();
|
||||
+ enum class DmaHeapFlag {
|
||||
+ Cma = (1 << 0),
|
||||
+ System = (1 << 1),
|
||||
+ Cma = 1 << 0,
|
||||
+ System = 1 << 1,
|
||||
+ };
|
||||
+
|
||||
+ using DmaHeapFlags = Flags<DmaHeapFlag>;
|
||||
|
@ -54,68 +56,114 @@ index cff8f140..22aa1007 100644
|
|||
+
|
||||
} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
|
||||
index 7444d9c2..177de31b 100644
|
||||
index 38ef175a..d0e33ce6 100644
|
||||
--- a/src/libcamera/dma_heaps.cpp
|
||||
+++ b/src/libcamera/dma_heaps.cpp
|
||||
@@ -16,6 +16,8 @@
|
||||
@@ -19,9 +19,11 @@
|
||||
|
||||
#include "libcamera/internal/dma_heaps.h"
|
||||
/**
|
||||
* \file dma_heaps.cpp
|
||||
- * \brief CMA dma-heap allocator
|
||||
+ * \brief dma-heap allocator
|
||||
*/
|
||||
|
||||
+namespace libcamera {
|
||||
+
|
||||
/*
|
||||
* /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
|
||||
* /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
|
||||
* to only have to worry about importing.
|
||||
@@ -23,28 +25,33 @@
|
||||
@@ -29,42 +31,77 @@
|
||||
* Annoyingly, should the cma heap size be specified on the kernel command line
|
||||
* instead of DT, the heap gets named "reserved" instead.
|
||||
*/
|
||||
-static constexpr std::array<const char *, 2> heapNames = {
|
||||
- "/dev/dma_heap/linux,cma",
|
||||
- "/dev/dma_heap/reserved"
|
||||
+static constexpr std::array<std::pair<DmaHeap::DmaHeapFlag, const char *>, 3> heapNames = {
|
||||
+ /* CMA heap names first */
|
||||
+ std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma"),
|
||||
+ std::make_pair(DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved"),
|
||||
+ std::make_pair(DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system")
|
||||
+
|
||||
+/**
|
||||
+ * \struct DmaHeapInfo
|
||||
+ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is
|
||||
+ * \var DmaHeapInfo::flag
|
||||
+ * \brief The type of the dma-heap
|
||||
+ * \var DmaHeapInfo::name
|
||||
+ * \brief The dma-heap's device node name
|
||||
+ */
|
||||
+struct DmaHeapInfo {
|
||||
+ DmaHeap::DmaHeapFlag flag;
|
||||
+ const char *name;
|
||||
};
|
||||
|
||||
-namespace libcamera {
|
||||
-
|
||||
+static constexpr std::array<DmaHeapInfo, 3> heapInfos = {
|
||||
+ { /* CMA heap names first */
|
||||
+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" },
|
||||
+ { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" },
|
||||
+ { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } }
|
||||
+};
|
||||
|
||||
LOG_DEFINE_CATEGORY(DmaHeap)
|
||||
|
||||
/**
|
||||
* \class DmaHeap
|
||||
- * \brief Helper class for CMA dma-heap allocations
|
||||
+ * \brief Helper class for dma-heap allocations
|
||||
*/
|
||||
|
||||
/**
|
||||
- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
|
||||
+ * \enum DmaHeap::DmaHeapFlag
|
||||
+ * \brief Type of the dma-heap
|
||||
+ * \var DmaHeap::Cma
|
||||
+ * \brief Allocate from a CMA dma-heap
|
||||
+ * \var DmaHeap::System
|
||||
+ * \brief Allocate from the system dma-heap
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \typedef DmaHeap::DmaHeapFlags
|
||||
+ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor
|
||||
+ * \param [in] flags The type(s) of the dma-heap(s) to allocate from
|
||||
*
|
||||
- * Goes through the internal list of possible names of the CMA dma-heap devices
|
||||
- * until a CMA dma-heap device is successfully opened. If it fails to open any
|
||||
- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
|
||||
- * object owns a wrapped dma-heap file descriptor.
|
||||
+ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes
|
||||
+ * through the internal list of possible names of the CMA and system dma-heap devices
|
||||
+ * until the dma-heap device of the requested type is successfully opened. If more
|
||||
+ * than one dma-heap type is specified in flags the CMA heap is tried first. If it
|
||||
+ * fails to open any dma-heap device an invalid DmaHeap object is constructed.
|
||||
+ * A valid DmaHeap object owns a wrapped dma-heap file descriptor.
|
||||
*
|
||||
* Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
|
||||
*/
|
||||
-DmaHeap::DmaHeap()
|
||||
+DmaHeap::DmaHeap(DmaHeapFlags flags)
|
||||
{
|
||||
- for (const char *name : heapNames) {
|
||||
- int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
|
||||
- if (ret < 0) {
|
||||
- ret = errno;
|
||||
- LOG(DmaHeap, Debug) << "Failed to open " << name << ": "
|
||||
- << strerror(ret);
|
||||
- continue;
|
||||
- }
|
||||
+ int ret;
|
||||
|
||||
- dmaHeapHandle_ = UniqueFD(ret);
|
||||
- break;
|
||||
+ for (const auto &name : heapNames) {
|
||||
+ if (flags & name.first) {
|
||||
+ ret = ::open(name.second, O_RDWR | O_CLOEXEC, 0);
|
||||
+ if (ret < 0) {
|
||||
+ ret = errno;
|
||||
+ LOG(DmaHeap, Debug) << "Failed to open " << name.second << ": "
|
||||
+ << strerror(ret);
|
||||
+ continue;
|
||||
+ }
|
||||
+ for (const auto &info : heapInfos) {
|
||||
+ if (!(flags & info.flag))
|
||||
+ continue;
|
||||
+
|
||||
+ LOG(DmaHeap, Debug) << "Using " << name.second;
|
||||
+ dmaHeapHandle_ = UniqueFD(ret);
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
+ int ret = ::open(info.name, O_RDWR | O_CLOEXEC, 0);
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
LOG(DmaHeap, Debug)
|
||||
- << "Failed to open " << name << ": "
|
||||
+ << "Failed to open " << info.name << ": "
|
||||
<< strerror(ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dmaHeapHandle_.isValid())
|
||||
+ LOG(DmaHeap, Debug) << "Using " << info.name;
|
||||
dmaHeapHandle_ = UniqueFD(ret);
|
||||
break;
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
From 006a4a31a6803e92ec67f48b66da2cdff8b2f6ab Mon Sep 17 00:00:00 2001
|
||||
From a6777760a2121f02808baecea504ac0e242f860b Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Sun, 29 Oct 2023 15:56:48 +0300
|
||||
Subject: [PATCH 04/25] libcamera: internal: Move SharedMemObject class to a
|
||||
Date: Mon, 11 Mar 2024 15:15:08 +0100
|
||||
Subject: [PATCH 04/21] libcamera: internal: Move SharedMemObject class to a
|
||||
common directory
|
||||
|
||||
Move SharedMemObject class out of RPi namespace and put it into
|
||||
include/libcamera/internal so that everyone could use it.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
---
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
.../common => include/libcamera/internal}/shared_mem_object.h | 4 ----
|
||||
2 files changed, 1 insertion(+), 4 deletions(-)
|
||||
rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (98%)
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
.../libcamera/internal}/shared_mem_object.h | 6 +-----
|
||||
2 files changed, 2 insertions(+), 5 deletions(-)
|
||||
rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (97%)
|
||||
|
||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
|
||||
index 33eb0fb3..5807dfd9 100644
|
||||
|
@ -30,12 +32,22 @@ index 33eb0fb3..5807dfd9 100644
|
|||
'sysfs.h',
|
||||
'v4l2_device.h',
|
||||
diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
|
||||
similarity index 98%
|
||||
similarity index 97%
|
||||
rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h
|
||||
rename to include/libcamera/internal/shared_mem_object.h
|
||||
index aa56c220..bfb639ee 100644
|
||||
index aa56c220..98636b44 100644
|
||||
--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
|
||||
+++ b/include/libcamera/internal/shared_mem_object.h
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
-#include <cstddef>
|
||||
#include <fcntl.h>
|
||||
+#include <stddef.h>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
namespace libcamera {
|
||||
|
@ -53,5 +65,5 @@ index aa56c220..bfb639ee 100644
|
|||
-
|
||||
} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
From cb9ff82efd82af8ae26b2aca4183928c74f7ef34 Mon Sep 17 00:00:00 2001
|
||||
From: Dennis Bonke <admin@dennisbonke.com>
|
||||
Date: Wed, 20 Dec 2023 16:22:29 +0100
|
||||
Subject: [PATCH 05/25] libcamera: internal: Document the SharedMemObject class
|
||||
|
||||
Document the SharedMemObject class.
|
||||
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
.../libcamera/internal/shared_mem_object.h | 53 +++++++++++++++++++
|
||||
1 file changed, 53 insertions(+)
|
||||
|
||||
diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
|
||||
index bfb639ee..e862ce48 100644
|
||||
--- a/include/libcamera/internal/shared_mem_object.h
|
||||
+++ b/include/libcamera/internal/shared_mem_object.h
|
||||
@@ -19,10 +19,20 @@
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
+/**
|
||||
+ * \class SharedMemObject
|
||||
+ * \brief Helper class for shared memory allocations.
|
||||
+ *
|
||||
+ * Takes a template T which is used to indicate the
|
||||
+ * data type of the object stored.
|
||||
+ */
|
||||
template<class T>
|
||||
class SharedMemObject
|
||||
{
|
||||
public:
|
||||
+ /**
|
||||
+ * \brief The size of the object that is going to be stored here.
|
||||
+ */
|
||||
static constexpr std::size_t SIZE = sizeof(T);
|
||||
|
||||
SharedMemObject()
|
||||
@@ -30,6 +40,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Contstructor for the SharedMemObject.
|
||||
+ * \param[in] name The requested name.
|
||||
+ * \param[in] args Any additional args.
|
||||
+ */
|
||||
template<class... Args>
|
||||
SharedMemObject(const std::string &name, Args &&...args)
|
||||
: name_(name), obj_(nullptr)
|
||||
@@ -57,6 +72,10 @@ public:
|
||||
obj_ = new (mem) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Move constructor for SharedMemObject.
|
||||
+ * \param[in] rhs The object to move.
|
||||
+ */
|
||||
SharedMemObject(SharedMemObject<T> &&rhs)
|
||||
{
|
||||
this->name_ = std::move(rhs.name_);
|
||||
@@ -76,6 +95,10 @@ public:
|
||||
/* Make SharedMemObject non-copyable for now. */
|
||||
LIBCAMERA_DISABLE_COPY(SharedMemObject)
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator= for SharedMemObject.
|
||||
+ * \param[in] rhs The SharedMemObject object to take the data from.
|
||||
+ */
|
||||
SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
|
||||
{
|
||||
this->name_ = std::move(rhs.name_);
|
||||
@@ -85,31 +108,61 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator-> for SharedMemObject.
|
||||
+ *
|
||||
+ * \return the object.
|
||||
+ */
|
||||
T *operator->()
|
||||
{
|
||||
return obj_;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator-> for SharedMemObject.
|
||||
+ *
|
||||
+ * \return the object.
|
||||
+ */
|
||||
const T *operator->() const
|
||||
{
|
||||
return obj_;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator* for SharedMemObject.
|
||||
+ *
|
||||
+ * \return the object.
|
||||
+ */
|
||||
T &operator*()
|
||||
{
|
||||
return *obj_;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator* for SharedMemObject.
|
||||
+ *
|
||||
+ * \return the object.
|
||||
+ */
|
||||
const T &operator*() const
|
||||
{
|
||||
return *obj_;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Gets the file descriptor for the underlaying storage file.
|
||||
+ *
|
||||
+ * \return the file descriptor.
|
||||
+ */
|
||||
const SharedFD &fd() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * \brief Operator bool() for SharedMemObject.
|
||||
+ *
|
||||
+ * \return true if the object is not null, false otherwise.
|
||||
+ */
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !!obj_;
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
|
||||
Date: Mon, 11 Mar 2024 15:15:09 +0100
|
||||
Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and
|
||||
document the SharedMemObject class
|
||||
|
||||
Split the parts which doesn't otherwise depend on the type T or
|
||||
arguments Args out of the SharedMemObject class into a new
|
||||
SharedMem class.
|
||||
|
||||
Doxygen documentation by Dennis Bonke and Andrei Konovalov.
|
||||
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
---
|
||||
.../libcamera/internal/shared_mem_object.h | 101 ++++++----
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/shared_mem_object.cpp | 190 ++++++++++++++++++
|
||||
3 files changed, 253 insertions(+), 39 deletions(-)
|
||||
create mode 100644 src/libcamera/shared_mem_object.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
|
||||
index 98636b44..43b07c9d 100644
|
||||
--- a/include/libcamera/internal/shared_mem_object.h
|
||||
+++ b/include/libcamera/internal/shared_mem_object.h
|
||||
@@ -6,12 +6,9 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
-#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
-#include <sys/stat.h>
|
||||
-#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#include <libcamera/base/class.h>
|
||||
@@ -19,58 +16,92 @@
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
+class SharedMem
|
||||
+{
|
||||
+public:
|
||||
+ SharedMem()
|
||||
+ : mem_(nullptr)
|
||||
+ {
|
||||
+ }
|
||||
+
|
||||
+ SharedMem(const std::string &name, std::size_t size);
|
||||
+
|
||||
+ SharedMem(SharedMem &&rhs)
|
||||
+ {
|
||||
+ this->name_ = std::move(rhs.name_);
|
||||
+ this->fd_ = std::move(rhs.fd_);
|
||||
+ this->mem_ = rhs.mem_;
|
||||
+ rhs.mem_ = nullptr;
|
||||
+ }
|
||||
+
|
||||
+ virtual ~SharedMem()
|
||||
+ {
|
||||
+ if (mem_)
|
||||
+ munmap(mem_, size_);
|
||||
+ }
|
||||
+
|
||||
+ /* Make SharedMem non-copyable for now. */
|
||||
+ LIBCAMERA_DISABLE_COPY(SharedMem)
|
||||
+
|
||||
+ SharedMem &operator=(SharedMem &&rhs)
|
||||
+ {
|
||||
+ this->name_ = std::move(rhs.name_);
|
||||
+ this->fd_ = std::move(rhs.fd_);
|
||||
+ this->mem_ = rhs.mem_;
|
||||
+ rhs.mem_ = nullptr;
|
||||
+ return *this;
|
||||
+ }
|
||||
+
|
||||
+ const SharedFD &fd() const
|
||||
+ {
|
||||
+ return fd_;
|
||||
+ }
|
||||
+
|
||||
+ void *mem() const
|
||||
+ {
|
||||
+ return mem_;
|
||||
+ }
|
||||
+
|
||||
+private:
|
||||
+ std::string name_;
|
||||
+ SharedFD fd_;
|
||||
+ size_t size_;
|
||||
+protected:
|
||||
+ void *mem_;
|
||||
+};
|
||||
+
|
||||
template<class T>
|
||||
-class SharedMemObject
|
||||
+class SharedMemObject : public SharedMem
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t SIZE = sizeof(T);
|
||||
|
||||
SharedMemObject()
|
||||
- : obj_(nullptr)
|
||||
+ : SharedMem(), obj_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
SharedMemObject(const std::string &name, Args &&...args)
|
||||
- : name_(name), obj_(nullptr)
|
||||
+ : SharedMem(name, SIZE), obj_(nullptr)
|
||||
{
|
||||
- void *mem;
|
||||
- int ret;
|
||||
-
|
||||
- ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
|
||||
- if (ret < 0)
|
||||
- return;
|
||||
-
|
||||
- fd_ = SharedFD(std::move(ret));
|
||||
- if (!fd_.isValid())
|
||||
- return;
|
||||
-
|
||||
- ret = ftruncate(fd_.get(), SIZE);
|
||||
- if (ret < 0)
|
||||
+ if (mem_ == nullptr)
|
||||
return;
|
||||
|
||||
- mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
- fd_.get(), 0);
|
||||
- if (mem == MAP_FAILED)
|
||||
- return;
|
||||
-
|
||||
- obj_ = new (mem) T(std::forward<Args>(args)...);
|
||||
+ obj_ = new (mem_) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
SharedMemObject(SharedMemObject<T> &&rhs)
|
||||
+ : SharedMem(std::move(rhs))
|
||||
{
|
||||
- this->name_ = std::move(rhs.name_);
|
||||
- this->fd_ = std::move(rhs.fd_);
|
||||
this->obj_ = rhs.obj_;
|
||||
rhs.obj_ = nullptr;
|
||||
}
|
||||
|
||||
~SharedMemObject()
|
||||
{
|
||||
- if (obj_) {
|
||||
+ if (obj_)
|
||||
obj_->~T();
|
||||
- munmap(obj_, SIZE);
|
||||
- }
|
||||
}
|
||||
|
||||
/* Make SharedMemObject non-copyable for now. */
|
||||
@@ -78,8 +109,7 @@ public:
|
||||
|
||||
SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
|
||||
{
|
||||
- this->name_ = std::move(rhs.name_);
|
||||
- this->fd_ = std::move(rhs.fd_);
|
||||
+ SharedMem::operator=(std::move(rhs));
|
||||
this->obj_ = rhs.obj_;
|
||||
rhs.obj_ = nullptr;
|
||||
return *this;
|
||||
@@ -105,19 +135,12 @@ public:
|
||||
return *obj_;
|
||||
}
|
||||
|
||||
- const SharedFD &fd() const
|
||||
- {
|
||||
- return fd_;
|
||||
- }
|
||||
-
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !!obj_;
|
||||
}
|
||||
|
||||
private:
|
||||
- std::string name_;
|
||||
- SharedFD fd_;
|
||||
T *obj_;
|
||||
};
|
||||
|
||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
|
||||
index 3c5e43df..94a95ae3 100644
|
||||
--- a/src/libcamera/meson.build
|
||||
+++ b/src/libcamera/meson.build
|
||||
@@ -41,6 +41,7 @@ libcamera_sources = files([
|
||||
'process.cpp',
|
||||
'pub_key.cpp',
|
||||
'request.cpp',
|
||||
+ 'shared_mem_object.cpp',
|
||||
'source_paths.cpp',
|
||||
'stream.cpp',
|
||||
'sysfs.cpp',
|
||||
diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp
|
||||
new file mode 100644
|
||||
index 00000000..44fe74c2
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/shared_mem_object.cpp
|
||||
@@ -0,0 +1,190 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Raspberry Pi Ltd
|
||||
+ *
|
||||
+ * shared_mem_object.cpp - Helper class for shared memory allocations
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/shared_mem_object.h"
|
||||
+
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+/**
|
||||
+ * \file shared_mem_object.cpp
|
||||
+ * \brief Helper class for shared memory allocations
|
||||
+ */
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class SharedMem
|
||||
+ * \brief Helper class for allocating shared memory
|
||||
+ *
|
||||
+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
|
||||
+ *
|
||||
+ * SharedMem allocates the shared memory of the given size and maps it.
|
||||
+ * To check that the shared memory was allocated and mapped successfully, one
|
||||
+ * needs to verify that the pointer to the shared memory returned by SharedMem::mem()
|
||||
+ * is not nullptr.
|
||||
+ *
|
||||
+ * To access the shared memory from another process the SharedFD should be passed
|
||||
+ * to that process, and then the shared memory should be mapped into that process
|
||||
+ * address space by calling mmap().
|
||||
+ *
|
||||
+ * A single memfd is created for every SharedMem. If there is a need to allocate
|
||||
+ * a large number of objects in shared memory, these objects should be grouped
|
||||
+ * together and use the shared memory allocated by a single SharedMem object if
|
||||
+ * possible. This will help to minimize the number of created memfd's.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size)
|
||||
+ * \brief Constructor for the SharedMem
|
||||
+ * \param[in] name Name of the SharedMem
|
||||
+ * \param[in] size Size of the shared memory to allocate and map
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMem::SharedMem(SharedMem &&rhs)
|
||||
+ * \brief Move constructor for SharedMem
|
||||
+ * \param[in] rhs The object to move
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMem::~SharedMem()
|
||||
+ * \brief SharedMem destructor
|
||||
+ *
|
||||
+ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use
|
||||
+ * count.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs)
|
||||
+ * \brief Move constructor for SharedMem
|
||||
+ * \param[in] rhs The object to move
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn const SharedFD &SharedMem::fd() const
|
||||
+ * \brief Gets the file descriptor for the underlying shared memory
|
||||
+ * \return The file descriptor
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn void *SharedMem::mem() const
|
||||
+ * \brief Gets the pointer to the underlying shared memory
|
||||
+ * \return The pointer to the shared memory
|
||||
+ */
|
||||
+
|
||||
+SharedMem::SharedMem(const std::string &name, std::size_t size)
|
||||
+ : name_(name), size_(size), mem_(nullptr)
|
||||
+{
|
||||
+ int fd = memfd_create(name_.c_str(), MFD_CLOEXEC);
|
||||
+ if (fd < 0)
|
||||
+ return;
|
||||
+
|
||||
+ fd_ = SharedFD(std::move(fd));
|
||||
+ if (!fd_.isValid())
|
||||
+ return;
|
||||
+
|
||||
+ if (ftruncate(fd_.get(), size_) < 0)
|
||||
+ return;
|
||||
+
|
||||
+ mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
+ fd_.get(), 0);
|
||||
+ if (mem_ == MAP_FAILED)
|
||||
+ mem_ = nullptr;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \var SharedMem::mem_
|
||||
+ * \brief Pointer to the shared memory allocated
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \class SharedMemObject
|
||||
+ * \brief Helper class for allocating objects in shared memory
|
||||
+ *
|
||||
+ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
|
||||
+ *
|
||||
+ * Given the type of the object to be created in shared memory and the arguments
|
||||
+ * to pass to this object's constructor, SharedMemObject allocates the shared memory
|
||||
+ * of the size of the object and constructs the object in this memory. To ensure
|
||||
+ * that the SharedMemObject was created successfully, one needs to verify that the
|
||||
+ * overloaded bool() operator returns true. The object created in the shared memory
|
||||
+ * can be accessed using the SharedMemObject::operator*() indirection operator. Its
|
||||
+ * members can be accessed with the SharedMemObject::operator->() member of pointer
|
||||
+ * operator.
|
||||
+ *
|
||||
+ * To access the object from another process the SharedFD should be passed to that
|
||||
+ * process, and the shared memory should be mapped by calling mmap().
|
||||
+ *
|
||||
+ * A single memfd is created for every SharedMemObject. If there is a need to allocate
|
||||
+ * a large number of objects in shared memory, these objects should be grouped into a
|
||||
+ * single large object to keep the number of created memfd's reasonably small.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \var SharedMemObject::SIZE
|
||||
+ * \brief The size of the object that is going to be stored here
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args)
|
||||
+ * \brief Constructor for the SharedMemObject
|
||||
+ * \param[in] name Name of the SharedMemObject
|
||||
+ * \param[in] args Args to pass to the constructor of the object in shared memory
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
|
||||
+ * \brief Move constructor for SharedMemObject
|
||||
+ * \param[in] rhs The object to move
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::~SharedMemObject()
|
||||
+ * \brief SharedMemObject destructor
|
||||
+ *
|
||||
+ * Destroys the object created in the shared memory and then unmaps the shared memory.
|
||||
+ * Decrements the shared memory descriptor use count.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
|
||||
+ * \brief Operator= for SharedMemObject
|
||||
+ * \param[in] rhs The SharedMemObject object to take the data from
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::operator->()
|
||||
+ * \brief Operator-> for SharedMemObject
|
||||
+ * \return The pointer to the object
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn const T *SharedMemObject::operator->() const
|
||||
+ * \brief Operator-> for SharedMemObject
|
||||
+ * \return The pointer to the const object
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::operator*()
|
||||
+ * \brief Operator* for SharedMemObject
|
||||
+ * \return The reference to the object
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn const T &SharedMemObject::operator*() const
|
||||
+ * \brief Operator* for SharedMemObject
|
||||
+ * \return Const reference to the object
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \fn SharedMemObject::operator bool()
|
||||
+ * \brief Operator bool() for SharedMemObject
|
||||
+ * \return True if the object was created OK in the shared memory, false otherwise
|
||||
+ */
|
||||
+
|
||||
+} // namespace libcamera
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -1,354 +0,0 @@
|
|||
From 3fa62a8e2f34c9794ba67e2565db8fef22938fa4 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Sun, 22 Oct 2023 17:49:32 +0300
|
||||
Subject: [PATCH 06/25] libcamera: introduce SoftwareIsp class
|
||||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
include/libcamera/internal/software_isp.h | 231 ++++++++++++++++++++++
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/software_isp.cpp | 62 ++++++
|
||||
4 files changed, 295 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp.h
|
||||
create mode 100644 src/libcamera/software_isp.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
|
||||
index 5807dfd9..1325941d 100644
|
||||
--- a/include/libcamera/internal/meson.build
|
||||
+++ b/include/libcamera/internal/meson.build
|
||||
@@ -40,6 +40,7 @@ libcamera_internal_headers = files([
|
||||
'pub_key.h',
|
||||
'request.h',
|
||||
'shared_mem_object.h',
|
||||
+ 'software_isp.h',
|
||||
'source_paths.h',
|
||||
'sysfs.h',
|
||||
'v4l2_device.h',
|
||||
diff --git a/include/libcamera/internal/software_isp.h b/include/libcamera/internal/software_isp.h
|
||||
new file mode 100644
|
||||
index 00000000..42ff48ec
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp.h
|
||||
@@ -0,0 +1,231 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * software_isp.h - Interface for a software implementation of an ISP
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <functional>
|
||||
+#include <initializer_list>
|
||||
+#include <map>
|
||||
+#include <memory>
|
||||
+#include <string>
|
||||
+#include <tuple>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include <libcamera/base/class.h>
|
||||
+#include <libcamera/base/log.h>
|
||||
+#include <libcamera/base/signal.h>
|
||||
+
|
||||
+#include <libcamera/geometry.h>
|
||||
+
|
||||
+#include "libcamera/internal/pipeline_handler.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class FrameBuffer;
|
||||
+class PixelFormat;
|
||||
+struct StreamConfiguration;
|
||||
+
|
||||
+LOG_DECLARE_CATEGORY(SoftwareIsp)
|
||||
+
|
||||
+/**
|
||||
+ * \brief Base class for the Software ISP.
|
||||
+ *
|
||||
+ * Base class of the SoftwareIsp interface.
|
||||
+ */
|
||||
+class SoftwareIsp
|
||||
+{
|
||||
+public:
|
||||
+ /**
|
||||
+ * \brief Constructor for the SoftwareIsp object.
|
||||
+ * \param[in] pipe The pipeline handler in use.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ */
|
||||
+ SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
|
||||
+ virtual ~SoftwareIsp();
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Load a configuration from a file.
|
||||
+ * \param[in] filename The file to load from.
|
||||
+ *
|
||||
+ * \return 0 on success.
|
||||
+ */
|
||||
+ virtual int loadConfiguration(const std::string &filename) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Gets if there is a valid debayer object.
|
||||
+ *
|
||||
+ * \returns true if there is, false otherwise.
|
||||
+ */
|
||||
+ virtual bool isValid() const = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the supported output formats.
|
||||
+ * \param[in] input The input format.
|
||||
+ *
|
||||
+ * \return all supported output formats or an empty vector if there are none.
|
||||
+ */
|
||||
+ virtual std::vector<PixelFormat> formats(PixelFormat input) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the supported output sizes for the given input format and size.
|
||||
+ * \param[in] inputFormat The input format.
|
||||
+ * \param[in] inputSize The input size.
|
||||
+ *
|
||||
+ * \return The valid size ranges or an empty range if there are none.
|
||||
+ */
|
||||
+ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the stride and the frame size.
|
||||
+ * \param[in] pixelFormat The output format.
|
||||
+ * \param[in] size The output size.
|
||||
+ *
|
||||
+ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
|
||||
+ */
|
||||
+ virtual std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Configure the SwIspSimple object according to the passed in parameters.
|
||||
+ * \param[in] inputCfg The input configuration.
|
||||
+ * \param[in] outputCfgs The output configurations.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno on failure.
|
||||
+ */
|
||||
+ virtual int configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
+ const ControlInfoMap &sensorControls) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Exports the buffers for use in processing.
|
||||
+ * \param[in] output The number of outputs requested.
|
||||
+ * \param[in] count The number of planes.
|
||||
+ * \param[out] buffers The exported buffers.
|
||||
+ *
|
||||
+ * \return count when successful, a negative return value if an error occurred.
|
||||
+ */
|
||||
+ virtual int exportBuffers(unsigned int output, unsigned int count,
|
||||
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Starts the Software ISP worker.
|
||||
+ *
|
||||
+ * \return 0 on success, any other value indicates an error.
|
||||
+ */
|
||||
+ virtual int start() = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Stops the Software ISP worker.
|
||||
+ */
|
||||
+ virtual void stop() = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Queues buffers for processing.
|
||||
+ * \param[in] input The input framebuffer.
|
||||
+ * \param[in] outputs The output framebuffers.
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno on failure
|
||||
+ */
|
||||
+ virtual int queueBuffers(FrameBuffer *input,
|
||||
+ const std::map<unsigned int, FrameBuffer *> &outputs) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process the statistics gathered.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ */
|
||||
+ virtual void processStats(const ControlList &sensorControls) = 0; // rather merge with queueBuffers()?
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the signal for when the sensor controls are set.
|
||||
+ *
|
||||
+ * \return The control list of the sensor controls.
|
||||
+ */
|
||||
+ virtual Signal<const ControlList &> &getSignalSetSensorControls() = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Signals that the input buffer is ready.
|
||||
+ */
|
||||
+ Signal<FrameBuffer *> inputBufferReady;
|
||||
+ /**
|
||||
+ * \brief Signals that the output buffer is ready.
|
||||
+ */
|
||||
+ Signal<FrameBuffer *> outputBufferReady;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Signals that the ISP stats are ready.
|
||||
+ *
|
||||
+ * The int parameter isn't actually used.
|
||||
+ */
|
||||
+ Signal<int> ispStatsReady;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * \brief Base class for the Software ISP Factory.
|
||||
+ *
|
||||
+ * Base class of the SoftwareIsp Factory.
|
||||
+ */
|
||||
+class SoftwareIspFactoryBase
|
||||
+{
|
||||
+public:
|
||||
+ SoftwareIspFactoryBase();
|
||||
+ virtual ~SoftwareIspFactoryBase() = default;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Creates a SoftwareIsp object.
|
||||
+ * \param[in] pipe The pipeline handler in use.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ *
|
||||
+ * \return An unique pointer to the created SoftwareIsp object.
|
||||
+ */
|
||||
+ static std::unique_ptr<SoftwareIsp> create(PipelineHandler *pipe,
|
||||
+ const ControlInfoMap &sensorControls);
|
||||
+ /**
|
||||
+ * \brief Gives back a pointer to the factory.
|
||||
+ *
|
||||
+ * \return A static pointer to the factory instance.
|
||||
+ */
|
||||
+ static SoftwareIspFactoryBase *&factory();
|
||||
+
|
||||
+private:
|
||||
+ LIBCAMERA_DISABLE_COPY_AND_MOVE(SoftwareIspFactoryBase)
|
||||
+
|
||||
+ static void registerType(SoftwareIspFactoryBase *factory);
|
||||
+ virtual std::unique_ptr<SoftwareIsp> createInstance(PipelineHandler *pipe,
|
||||
+ const ControlInfoMap &sensorControls) const = 0;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * \brief Implementation for the Software ISP Factory.
|
||||
+ */
|
||||
+template<typename _SoftwareIsp>
|
||||
+class SoftwareIspFactory : public SoftwareIspFactoryBase
|
||||
+{
|
||||
+public:
|
||||
+ SoftwareIspFactory()
|
||||
+ : SoftwareIspFactoryBase()
|
||||
+ {
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Creates an instance of a SoftwareIsp object.
|
||||
+ * \param[in] pipe The pipeline handler in use.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ *
|
||||
+ * \return An unique pointer to the created SoftwareIsp object.
|
||||
+ */
|
||||
+ std::unique_ptr<SoftwareIsp> createInstance(PipelineHandler *pipe,
|
||||
+ const ControlInfoMap &sensorControls) const override
|
||||
+ {
|
||||
+ return std::make_unique<_SoftwareIsp>(pipe, sensorControls);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+#define REGISTER_SOFTWAREISP(softwareIsp) \
|
||||
+ static SoftwareIspFactory<softwareIsp> global_##softwareIsp##Factory;
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
|
||||
index 3c5e43df..86494663 100644
|
||||
--- a/src/libcamera/meson.build
|
||||
+++ b/src/libcamera/meson.build
|
||||
@@ -41,6 +41,7 @@ libcamera_sources = files([
|
||||
'process.cpp',
|
||||
'pub_key.cpp',
|
||||
'request.cpp',
|
||||
+ 'software_isp.cpp',
|
||||
'source_paths.cpp',
|
||||
'stream.cpp',
|
||||
'sysfs.cpp',
|
||||
diff --git a/src/libcamera/software_isp.cpp b/src/libcamera/software_isp.cpp
|
||||
new file mode 100644
|
||||
index 00000000..2ff97d70
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp.cpp
|
||||
@@ -0,0 +1,62 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * software_isp.cpp - Interface for a software implementation of an ISP
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp.h"
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(SoftwareIsp)
|
||||
+
|
||||
+SoftwareIsp::SoftwareIsp([[maybe_unused]] PipelineHandler *pipe,
|
||||
+ [[maybe_unused]] const ControlInfoMap &sensorControls)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+SoftwareIsp::~SoftwareIsp()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+/* SoftwareIspFactoryBase */
|
||||
+
|
||||
+SoftwareIspFactoryBase::SoftwareIspFactoryBase()
|
||||
+{
|
||||
+ registerType(this);
|
||||
+}
|
||||
+
|
||||
+void SoftwareIspFactoryBase::registerType(SoftwareIspFactoryBase *factory)
|
||||
+{
|
||||
+ SoftwareIspFactoryBase *®istered =
|
||||
+ SoftwareIspFactoryBase::factory();
|
||||
+
|
||||
+ ASSERT(!registered && factory);
|
||||
+ registered = factory;
|
||||
+}
|
||||
+
|
||||
+SoftwareIspFactoryBase *&SoftwareIspFactoryBase::factory()
|
||||
+{
|
||||
+ static SoftwareIspFactoryBase *factory;
|
||||
+ return factory;
|
||||
+}
|
||||
+
|
||||
+std::unique_ptr<SoftwareIsp>
|
||||
+SoftwareIspFactoryBase::create(PipelineHandler *pipe,
|
||||
+ const ControlInfoMap &sensorControls)
|
||||
+{
|
||||
+ SoftwareIspFactoryBase *factory = SoftwareIspFactoryBase::factory();
|
||||
+ if (!factory)
|
||||
+ return nullptr;
|
||||
+
|
||||
+ std::unique_ptr<SoftwareIsp> swIsp = factory->createInstance(pipe, sensorControls);
|
||||
+ if (swIsp->isValid())
|
||||
+ return swIsp;
|
||||
+
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -0,0 +1,523 @@
|
|||
From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Mon, 11 Mar 2024 15:15:10 +0100
|
||||
Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class
|
||||
|
||||
Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
|
||||
|
||||
This implementation offers a configure function + functions to gather
|
||||
statistics on a line by line basis. This allows CPU based software
|
||||
debayering to call into interlace debayering and statistics gathering
|
||||
on a line by line bases while the input data is still hot in the cache.
|
||||
|
||||
This implementation also allows specifying a window over which to gather
|
||||
statistics instead of processing the whole frame.
|
||||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-developed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-developed-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Co-developed-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
.../internal/software_isp/meson.build | 5 +
|
||||
.../internal/software_isp/swisp_stats.h | 38 ++++
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/software_isp/meson.build | 12 +
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 208 ++++++++++++++++++
|
||||
src/libcamera/software_isp/swstats_cpu.h | 159 +++++++++++++
|
||||
7 files changed, 424 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/meson.build
|
||||
create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h
|
||||
create mode 100644 src/libcamera/software_isp/meson.build
|
||||
create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
|
||||
create mode 100644 src/libcamera/software_isp/swstats_cpu.h
|
||||
|
||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
|
||||
index 5807dfd9..160fdc37 100644
|
||||
--- a/include/libcamera/internal/meson.build
|
||||
+++ b/include/libcamera/internal/meson.build
|
||||
@@ -50,3 +50,4 @@ libcamera_internal_headers = files([
|
||||
])
|
||||
|
||||
subdir('converter')
|
||||
+subdir('software_isp')
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..66c9c3fb
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -0,0 +1,5 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+libcamera_internal_headers += files([
|
||||
+ 'swisp_stats.h',
|
||||
+])
|
||||
diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
new file mode 100644
|
||||
index 00000000..afe42c9a
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
@@ -0,0 +1,38 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * swisp_stats.h - Statistics data format used by the software ISP and software IPA
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \brief Struct that holds the statistics for the Software ISP.
|
||||
+ */
|
||||
+struct SwIspStats {
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled red pixels.
|
||||
+ */
|
||||
+ unsigned long sumR_;
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled green pixels.
|
||||
+ */
|
||||
+ unsigned long sumG_;
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled blue pixels.
|
||||
+ */
|
||||
+ unsigned long sumB_;
|
||||
+ /**
|
||||
+ * \brief Number of bins in the yHistogram.
|
||||
+ */
|
||||
+ static constexpr unsigned int kYHistogramSize = 16;
|
||||
+ /**
|
||||
+ * \brief A histogram of luminance values.
|
||||
+ */
|
||||
+ std::array<unsigned int, kYHistogramSize> yHistogram;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
|
||||
index 94a95ae3..91e4cc60 100644
|
||||
--- a/src/libcamera/meson.build
|
||||
+++ b/src/libcamera/meson.build
|
||||
@@ -71,6 +71,7 @@ subdir('converter')
|
||||
subdir('ipa')
|
||||
subdir('pipeline')
|
||||
subdir('proxy')
|
||||
+subdir('software_isp')
|
||||
|
||||
null_dep = dependency('', required : false)
|
||||
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..fcfff74a
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -0,0 +1,12 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+softisp_enabled = pipelines.contains('simple')
|
||||
+summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')
|
||||
+
|
||||
+if not (softisp_enabled)
|
||||
+ subdir_done()
|
||||
+endif
|
||||
+
|
||||
+libcamera_sources += files([
|
||||
+ 'swstats_cpu.cpp',
|
||||
+])
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
new file mode 100644
|
||||
index 00000000..448d0e4c
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
@@ -0,0 +1,208 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats_cpu.cpp - CPU based software statistics implementation
|
||||
+ */
|
||||
+
|
||||
+#include "swstats_cpu.h"
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+#include <libcamera/stream.h>
|
||||
+
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class SwStatsCpu
|
||||
+ * \brief Class for gathering statistics on the CPU
|
||||
+ *
|
||||
+ * CPU based software ISP statistics implementation.
|
||||
+ *
|
||||
+ * This class offers a configure function + functions to gather statistics
|
||||
+ * on a line by line basis. This allows CPU based software debayering to
|
||||
+ * interlace debayering and statistics gathering on a line by line basis
|
||||
+ * while the input data is still hot in the cache.
|
||||
+ *
|
||||
+ * It is also possible to specify a window over which to gather
|
||||
+ * statistics instead of processing the whole frame.
|
||||
+ */
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(SwStatsCpu)
|
||||
+
|
||||
+SwStatsCpu::SwStatsCpu()
|
||||
+{
|
||||
+ sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
|
||||
+ if (!sharedStats_.fd().isValid())
|
||||
+ LOG(SwStatsCpu, Error)
|
||||
+ << "Failed to create shared memory for statistics";
|
||||
+}
|
||||
+
|
||||
+static const unsigned int kRedYMul = 77; /* 0.299 * 256 */
|
||||
+static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */
|
||||
+static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
|
||||
+
|
||||
+#define SWSTATS_START_LINE_STATS(pixel_t) \
|
||||
+ pixel_t r, g, g2, b; \
|
||||
+ unsigned int yVal; \
|
||||
+ \
|
||||
+ unsigned int sumR = 0; \
|
||||
+ unsigned int sumG = 0; \
|
||||
+ unsigned int sumB = 0;
|
||||
+
|
||||
+#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
|
||||
+ sumR += r; \
|
||||
+ sumG += g; \
|
||||
+ sumB += b; \
|
||||
+ \
|
||||
+ yVal = r * kRedYMul; \
|
||||
+ yVal += g * kGreenYMul; \
|
||||
+ yVal += b * kBlueYMul; \
|
||||
+ stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
|
||||
+
|
||||
+#define SWSTATS_FINISH_LINE_STATS() \
|
||||
+ stats_.sumR_ += sumR; \
|
||||
+ stats_.sumG_ += sumG; \
|
||||
+ stats_.sumB_ += sumB;
|
||||
+
|
||||
+void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
|
||||
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
|
||||
+ const int widthInBytes = window_.width * 5 / 4;
|
||||
+
|
||||
+ if (swapLines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ SWSTATS_START_LINE_STATS(uint8_t)
|
||||
+
|
||||
+ /* x += 5 sample every other 2x2 block */
|
||||
+ for (int x = 0; x < widthInBytes; x += 5) {
|
||||
+ /* BGGR */
|
||||
+ b = src0[x];
|
||||
+ g = src0[x + 1];
|
||||
+ g2 = src1[x];
|
||||
+ r = src1[x + 1];
|
||||
+ g = (g + g2) / 2;
|
||||
+ /* Data is already 8 bits, divide by 1 */
|
||||
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
|
||||
+ }
|
||||
+
|
||||
+ SWSTATS_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
|
||||
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
|
||||
+ const int widthInBytes = window_.width * 5 / 4;
|
||||
+
|
||||
+ if (swapLines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ SWSTATS_START_LINE_STATS(uint8_t)
|
||||
+
|
||||
+ /* x += 5 sample every other 2x2 block */
|
||||
+ for (int x = 0; x < widthInBytes; x += 5) {
|
||||
+ /* GBRG */
|
||||
+ g = src0[x];
|
||||
+ b = src0[x + 1];
|
||||
+ r = src1[x];
|
||||
+ g2 = src1[x + 1];
|
||||
+ g = (g + g2) / 2;
|
||||
+ /* Data is already 8 bits, divide by 1 */
|
||||
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
|
||||
+ }
|
||||
+
|
||||
+ SWSTATS_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Reset state to start statistics gathering for a new frame.
|
||||
+ *
|
||||
+ * This may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+void SwStatsCpu::startFrame(void)
|
||||
+{
|
||||
+ stats_.sumR_ = 0;
|
||||
+ stats_.sumB_ = 0;
|
||||
+ stats_.sumG_ = 0;
|
||||
+ stats_.yHistogram.fill(0);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Finish statistics calculation for the current frame.
|
||||
+ *
|
||||
+ * This may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+void SwStatsCpu::finishFrame(void)
|
||||
+{
|
||||
+ *sharedStats_ = stats_;
|
||||
+ statsReady.emit(0);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Configure the statistics object for the passed in input format.
|
||||
+ * \param[in] inputCfg The input format
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno value on failure
|
||||
+ */
|
||||
+int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
+{
|
||||
+ BayerFormat bayerFormat =
|
||||
+ BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
|
||||
+
|
||||
+ if (bayerFormat.bitDepth == 10 &&
|
||||
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
|
||||
+ patternSize_.height = 2;
|
||||
+ patternSize_.width = 4; /* 5 bytes per *4* pixels */
|
||||
+ /* Skip every 3th and 4th line, sample every other 2x2 block */
|
||||
+ ySkipMask_ = 0x02;
|
||||
+ xShift_ = 0;
|
||||
+
|
||||
+ switch (bayerFormat.order) {
|
||||
+ case BayerFormat::BGGR:
|
||||
+ case BayerFormat::GRBG:
|
||||
+ stats0_ = &SwStatsCpu::statsBGGR10PLine0;
|
||||
+ swapLines_ = bayerFormat.order == BayerFormat::GRBG;
|
||||
+ return 0;
|
||||
+ case BayerFormat::GBRG:
|
||||
+ case BayerFormat::RGGB:
|
||||
+ stats0_ = &SwStatsCpu::statsGBRG10PLine0;
|
||||
+ swapLines_ = bayerFormat.order == BayerFormat::RGGB;
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOG(SwStatsCpu, Info)
|
||||
+ << "Unsupported input format " << inputCfg.pixelFormat.toString();
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Specify window coordinates over which to gather statistics.
|
||||
+ * \param[in] window The window object.
|
||||
+ */
|
||||
+void SwStatsCpu::setWindow(Rectangle window)
|
||||
+{
|
||||
+ window_ = window;
|
||||
+
|
||||
+ window_.x &= ~(patternSize_.width - 1);
|
||||
+ window_.x += xShift_;
|
||||
+ window_.y &= ~(patternSize_.height - 1);
|
||||
+
|
||||
+ /* width_ - xShift_ to make sure the window fits */
|
||||
+ window_.width -= xShift_;
|
||||
+ window_.width &= ~(patternSize_.width - 1);
|
||||
+ window_.height &= ~(patternSize_.height - 1);
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
|
||||
new file mode 100644
|
||||
index 00000000..0ac9ae71
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.h
|
||||
@@ -0,0 +1,159 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats_cpu.h - CPU based software statistics implementation
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <stdint.h>
|
||||
+
|
||||
+#include <libcamera/base/signal.h>
|
||||
+
|
||||
+#include <libcamera/geometry.h>
|
||||
+
|
||||
+#include "libcamera/internal/shared_mem_object.h"
|
||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class PixelFormat;
|
||||
+struct StreamConfiguration;
|
||||
+
|
||||
+class SwStatsCpu
|
||||
+{
|
||||
+public:
|
||||
+ SwStatsCpu();
|
||||
+ ~SwStatsCpu() = default;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Gets whether the statistics object is valid.
|
||||
+ *
|
||||
+ * \return true if it's valid, false otherwise
|
||||
+ */
|
||||
+ bool isValid() const { return sharedStats_.fd().isValid(); }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the file descriptor for the statistics.
|
||||
+ *
|
||||
+ * \return the file descriptor
|
||||
+ */
|
||||
+ const SharedFD &getStatsFD() { return sharedStats_.fd(); }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the pattern size.
|
||||
+ *
|
||||
+ * For some input-formats, e.g. Bayer data, processing is done multiple lines
|
||||
+ * and/or columns at a time. Get width and height at which the (bayer) pattern
|
||||
+ * repeats. Window values are rounded down to a multiple of this and the height
|
||||
+ * also indicates if processLine2() should be called or not.
|
||||
+ * This may only be called after a successful configure() call.
|
||||
+ *
|
||||
+ * \return the pattern size
|
||||
+ */
|
||||
+ const Size &patternSize() { return patternSize_; }
|
||||
+
|
||||
+ int configure(const StreamConfiguration &inputCfg);
|
||||
+ void setWindow(Rectangle window);
|
||||
+ void startFrame();
|
||||
+ void finishFrame();
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process line 0.
|
||||
+ * \param[in] y The y coordinate.
|
||||
+ * \param[in] src The input data.
|
||||
+ *
|
||||
+ * This function processes line 0 for input formats with patternSize height == 1.
|
||||
+ * It'll process line 0 and 1 for input formats with patternSize height >= 2.
|
||||
+ * This function may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void processLine0(unsigned int y, const uint8_t *src[])
|
||||
+ {
|
||||
+ if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
|
||||
+ y >= (window_.y + window_.height))
|
||||
+ return;
|
||||
+
|
||||
+ (this->*stats0_)(src);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process line 2 and 3.
|
||||
+ * \param[in] y The y coordinate.
|
||||
+ * \param[in] src The input data.
|
||||
+ *
|
||||
+ * This function processes line 2 and 3 for input formats with patternSize height == 4.
|
||||
+ * This function may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void processLine2(unsigned int y, const uint8_t *src[])
|
||||
+ {
|
||||
+ if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
|
||||
+ y >= (window_.y + window_.height))
|
||||
+ return;
|
||||
+
|
||||
+ (this->*stats2_)(src);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Signals that the statistics are ready.
|
||||
+ *
|
||||
+ * The int parameter isn't actually used.
|
||||
+ */
|
||||
+ Signal<int> statsReady;
|
||||
+
|
||||
+private:
|
||||
+ /**
|
||||
+ * \brief Called when there is data to get statistics from.
|
||||
+ * \param[in] src The input data
|
||||
+ *
|
||||
+ * These functions take an array of (patternSize_.height + 1) src
|
||||
+ * pointers each pointing to a line in the source image. The middle
|
||||
+ * element of the array will point to the actual line being processed.
|
||||
+ * Earlier element(s) will point to the previous line(s) and later
|
||||
+ * element(s) to the next line(s).
|
||||
+ *
|
||||
+ * See the documentation of DebayerCpu::debayerFn for more details.
|
||||
+ */
|
||||
+ using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
|
||||
+
|
||||
+ void statsBGGR10PLine0(const uint8_t *src[]);
|
||||
+ void statsGBRG10PLine0(const uint8_t *src[]);
|
||||
+
|
||||
+ /* Variables set by configure(), used every line */
|
||||
+ statsProcessFn stats0_;
|
||||
+ statsProcessFn stats2_;
|
||||
+ bool swapLines_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Skip lines where this bitmask is set in y.
|
||||
+ */
|
||||
+ unsigned int ySkipMask_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Statistics window, set by setWindow(), used ever line.
|
||||
+ */
|
||||
+ Rectangle window_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief The size of the bayer pattern.
|
||||
+ *
|
||||
+ * Valid sizes are: 2x2, 4x2 or 4x4.
|
||||
+ */
|
||||
+ Size patternSize_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief The offset of x, applied to window_.x for bayer variants.
|
||||
+ *
|
||||
+ * This can either be 0 or 1.
|
||||
+ */
|
||||
+ unsigned int xShift_;
|
||||
+
|
||||
+ SharedMemObject<SwIspStats> sharedStats_;
|
||||
+ SwIspStats stats_;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From 8fc77447c0d76b0b52b19d23674049181c6cf8d2 Mon Sep 17 00:00:00 2001
|
||||
From 25e6893e46bd2174f6913eea79817988d9280706 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Mon, 11 Dec 2023 14:46:53 +0100
|
||||
Subject: [PATCH 09/25] libcamera: software_isp: Add Debayer base class
|
||||
Date: Mon, 11 Mar 2024 15:15:11 +0100
|
||||
Subject: [PATCH 07/21] libcamera: software_isp: Add Debayer base class
|
||||
|
||||
Add a base class for debayer implementations. This is intended to be
|
||||
suitable for both GPU (or otherwise) accelerated debayer implementations
|
||||
|
@ -9,37 +9,139 @@ as well as CPU based debayering.
|
|||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../libcamera/internal/software_isp/debayer.h | 132 ++++++++++++++++++
|
||||
.../internal/software_isp/debayer_params.h | 43 ++++++
|
||||
.../internal/software_isp/meson.build | 2 +
|
||||
src/libcamera/software_isp/debayer.cpp | 22 +++
|
||||
.../internal/software_isp/debayer_params.h | 48 ++++++++
|
||||
.../internal/software_isp/meson.build | 1 +
|
||||
src/libcamera/software_isp/debayer.cpp | 29 +++++
|
||||
src/libcamera/software_isp/debayer.h | 104 ++++++++++++++++++
|
||||
src/libcamera/software_isp/meson.build | 1 +
|
||||
5 files changed, 200 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/debayer.h
|
||||
5 files changed, 183 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/debayer_params.h
|
||||
create mode 100644 src/libcamera/software_isp/debayer.cpp
|
||||
create mode 100644 src/libcamera/software_isp/debayer.h
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
new file mode 100644
|
||||
index 00000000..39e6f393
|
||||
index 00000000..98965fa1
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/debayer.h
|
||||
@@ -0,0 +1,132 @@
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
@@ -0,0 +1,48 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer_params.h - DebayerParams header
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \brief Struct to hold the debayer parameters.
|
||||
+ */
|
||||
+struct DebayerParams {
|
||||
+ /**
|
||||
+ * \brief const value for 1.0 gain
|
||||
+ */
|
||||
+ static constexpr unsigned int kGain10 = 256;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Red Gain
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainR;
|
||||
+ /**
|
||||
+ * \brief Green Gain
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainG;
|
||||
+ /**
|
||||
+ * \brief Blue Gain
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainB;
|
||||
+ /**
|
||||
+ * \brief Gamma correction, 1.0 is no correction
|
||||
+ */
|
||||
+ float gamma;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index 66c9c3fb..a620e16d 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
libcamera_internal_headers += files([
|
||||
+ 'debayer_params.h',
|
||||
'swisp_stats.h',
|
||||
])
|
||||
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
|
||||
new file mode 100644
|
||||
index 00000000..64f0b5a0
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/debayer.cpp
|
||||
@@ -0,0 +1,29 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer.cpp - debayer base class
|
||||
+ */
|
||||
+
|
||||
+#include "debayer.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class Debayer
|
||||
+ * \brief Base debayering class
|
||||
+ *
|
||||
+ * Base class that provides functions for setting up the debayering process.
|
||||
+ */
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(Debayer)
|
||||
+
|
||||
+Debayer::~Debayer()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
|
||||
new file mode 100644
|
||||
index 00000000..8880ff99
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/debayer.h
|
||||
@@ -0,0 +1,104 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer.h - debayering base class
|
||||
+ */
|
||||
|
@ -62,12 +164,6 @@ index 00000000..39e6f393
|
|||
+
|
||||
+LOG_DECLARE_CATEGORY(Debayer)
|
||||
+
|
||||
+/**
|
||||
+ * \class Debayer
|
||||
+ * \brief Base debayering class
|
||||
+ *
|
||||
+ * Base class that provides functions for setting up the debayering process.
|
||||
+ */
|
||||
+class Debayer
|
||||
+{
|
||||
+public:
|
||||
|
@ -87,6 +183,8 @@ index 00000000..39e6f393
|
|||
+ * \brief Get the width and height at which the bayer pattern repeats.
|
||||
+ * \param[in] inputFormat The input format.
|
||||
+ *
|
||||
+ * Valid sizes are: 2x2, 4x2 or 4x4.
|
||||
+ *
|
||||
+ * \return pattern size or an empty size for unsupported inputFormats.
|
||||
+ */
|
||||
+ virtual Size patternSize(PixelFormat inputFormat) = 0;
|
||||
|
@ -107,7 +205,7 @@ index 00000000..39e6f393
|
|||
+ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
|
||||
+ */
|
||||
+ virtual std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process the bayer data into the requested format.
|
||||
|
@ -127,31 +225,7 @@ index 00000000..39e6f393
|
|||
+ *
|
||||
+ * \return The valid size ranges or an empty range if there are none.
|
||||
+ */
|
||||
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
+ {
|
||||
+ Size pattern_size = patternSize(inputFormat);
|
||||
+
|
||||
+ if (pattern_size.isNull())
|
||||
+ return {};
|
||||
+
|
||||
+ /*
|
||||
+ * For debayer interpolation a border of pattern-height x pattern-width
|
||||
+ * is kept around the entire image. Combined with a minimum-size of
|
||||
+ * pattern-height x pattern-width this means the input-size needs to be
|
||||
+ * at least (3 * pattern-height) x (3 * pattern-width).
|
||||
+ */
|
||||
+ if (inputSize.width < (3 * pattern_size.width) ||
|
||||
+ inputSize.height < (3 * pattern_size.height)) {
|
||||
+ LOG(Debayer, Warning)
|
||||
+ << "Input format size too small: " << inputSize.toString();
|
||||
+ return {};
|
||||
+ }
|
||||
+
|
||||
+ return SizeRange(Size(pattern_size.width, pattern_size.height),
|
||||
+ Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
|
||||
+ (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1)),
|
||||
+ pattern_size.width, pattern_size.height);
|
||||
+ }
|
||||
+ virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Signals when the input buffer is ready.
|
||||
|
@ -165,108 +239,17 @@ index 00000000..39e6f393
|
|||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
new file mode 100644
|
||||
index 00000000..8f515304
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
@@ -0,0 +1,43 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats.h - software statistics base class
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \brief Struct to hold the debayer parameters.
|
||||
+ */
|
||||
+struct DebayerParams {
|
||||
+ /**
|
||||
+ * \brief Red Gain.
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainR;
|
||||
+ /**
|
||||
+ * \brief Green Gain.
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainG;
|
||||
+ /**
|
||||
+ * \brief Blue Gain.
|
||||
+ *
|
||||
+ * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
|
||||
+ */
|
||||
+ unsigned int gainB;
|
||||
+ /**
|
||||
+ * \brief Gamma correction, 1.0 is no correction.
|
||||
+ */
|
||||
+ float gamma;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index 1d9e4018..7e40925e 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -1,6 +1,8 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
libcamera_internal_headers += files([
|
||||
+ 'debayer.h',
|
||||
+ 'debayer_params.h',
|
||||
'swisp_stats.h',
|
||||
'swstats.h',
|
||||
'swstats_cpu.h',
|
||||
diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
|
||||
new file mode 100644
|
||||
index 00000000..442da1ac
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/debayer.cpp
|
||||
@@ -0,0 +1,22 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer.cpp - debayer base class
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/debayer.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(Debayer)
|
||||
+
|
||||
+Debayer::~Debayer()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
index d31c6217..d4ae5ac7 100644
|
||||
index fcfff74a..62095f61 100644
|
||||
--- a/src/libcamera/software_isp/meson.build
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
@@ -8,5 +8,6 @@ if not (softisp_enabled)
|
||||
endif
|
||||
|
||||
libcamera_sources += files([
|
||||
+ 'debayer.cpp',
|
||||
'swstats.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
+ 'debayer.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
])
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,382 +0,0 @@
|
|||
From ca3bb6ddf5307537aa05e43d3ec1ff7ffdc0efed Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Thu, 7 Dec 2023 13:30:27 +0100
|
||||
Subject: [PATCH 07/25] libcamera: software_isp: Add SwStats base class
|
||||
|
||||
Add a virtual base class for CPU based software statistics gathering
|
||||
implementations.
|
||||
|
||||
The idea is for the implementations to offer a configure function +
|
||||
functions to gather statistics on a line by line basis. This allows
|
||||
CPU based software debayering to call into interlace debayering and
|
||||
statistics gathering on a line by line bases while the input data
|
||||
is still hot in the cache.
|
||||
|
||||
This base class also allows the user of an implementation to specify
|
||||
a window over which to gather statistics instead of processing the
|
||||
whole frame; and it allows the implementation to choose to only
|
||||
process 1/2, 1/4th, etc. of the lines instead of processing all
|
||||
lines (in the window) by setting y_skip_mask_ from configure().
|
||||
Skipping columns is left up the line-processing functions provided
|
||||
by the implementation.
|
||||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
include/libcamera/internal/meson.build | 1 +
|
||||
.../internal/software_isp/meson.build | 6 +
|
||||
.../internal/software_isp/swisp_stats.h | 34 +++
|
||||
.../libcamera/internal/software_isp/swstats.h | 215 ++++++++++++++++++
|
||||
src/libcamera/meson.build | 1 +
|
||||
src/libcamera/software_isp/meson.build | 5 +
|
||||
src/libcamera/software_isp/swstats.cpp | 22 ++
|
||||
7 files changed, 284 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/meson.build
|
||||
create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h
|
||||
create mode 100644 include/libcamera/internal/software_isp/swstats.h
|
||||
create mode 100644 src/libcamera/software_isp/meson.build
|
||||
create mode 100644 src/libcamera/software_isp/swstats.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
|
||||
index 1325941d..caa533c4 100644
|
||||
--- a/include/libcamera/internal/meson.build
|
||||
+++ b/include/libcamera/internal/meson.build
|
||||
@@ -51,3 +51,4 @@ libcamera_internal_headers = files([
|
||||
])
|
||||
|
||||
subdir('converter')
|
||||
+subdir('software_isp')
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..1c43acc4
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -0,0 +1,6 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+libcamera_internal_headers += files([
|
||||
+ 'swisp_stats.h',
|
||||
+ 'swstats.h',
|
||||
+])
|
||||
diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
new file mode 100644
|
||||
index 00000000..07ba7d6a
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
@@ -0,0 +1,34 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * swisp_stats.h - Statistics data format used by the software ISP and software IPA
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \brief Struct that holds the statistics for the Software ISP.
|
||||
+ */
|
||||
+struct SwIspStats {
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled red pixels.
|
||||
+ */
|
||||
+ unsigned long sumR_;
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled green pixels.
|
||||
+ */
|
||||
+ unsigned long sumG_;
|
||||
+ /**
|
||||
+ * \brief Holds the sum of all sampled blue pixels.
|
||||
+ */
|
||||
+ unsigned long sumB_;
|
||||
+ /**
|
||||
+ * \brief A histogram of luminance values.
|
||||
+ */
|
||||
+ unsigned int y_histogram[16];
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/swstats.h b/include/libcamera/internal/software_isp/swstats.h
|
||||
new file mode 100644
|
||||
index 00000000..dcac7064
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/swstats.h
|
||||
@@ -0,0 +1,215 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats.h - software statistics base class
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <stdint.h>
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+#include <libcamera/base/signal.h>
|
||||
+
|
||||
+#include <libcamera/geometry.h>
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class PixelFormat;
|
||||
+struct SharedFD;
|
||||
+struct StreamConfiguration;
|
||||
+
|
||||
+LOG_DECLARE_CATEGORY(SwStats)
|
||||
+
|
||||
+/**
|
||||
+ * \class SwStats
|
||||
+ * \brief Base class for the software ISP statistics.
|
||||
+ *
|
||||
+ * Base class for the software ISP statistics.
|
||||
+ */
|
||||
+class SwStats
|
||||
+{
|
||||
+public:
|
||||
+ virtual ~SwStats() = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Gets wether the statistics object is valid.
|
||||
+ *
|
||||
+ * \return true if it's valid, false otherwise.
|
||||
+ */
|
||||
+ virtual bool isValid() const = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Configure the statistics object for the passed in input format.
|
||||
+ * \param[in] inputCfg The input format
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno value on failure.
|
||||
+ */
|
||||
+ virtual int configure(const StreamConfiguration &inputCfg) = 0;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the file descriptor for the statistics.
|
||||
+ *
|
||||
+ * \return the file descriptor
|
||||
+ */
|
||||
+ virtual const SharedFD &getStatsFD() = 0;
|
||||
+
|
||||
+protected:
|
||||
+ /**
|
||||
+ * \brief Called when there is data to get statistics from.
|
||||
+ * \param[in] src The input data
|
||||
+ */
|
||||
+ typedef void (SwStats::*statsProcessFn)(const uint8_t *src[]);
|
||||
+ /**
|
||||
+ * \brief Called when the statistics gathering is done or when a new frame starts.
|
||||
+ */
|
||||
+ typedef void (SwStats::*statsVoidFn)();
|
||||
+
|
||||
+ /* Variables set by configure(), used every line */
|
||||
+ /**
|
||||
+ * \brief The function called when a line is ready for statistics processing.
|
||||
+ *
|
||||
+ * Used for line 0 and 1, repeating if there isn't a 3rd and a 4th line in the bayer order.
|
||||
+ */
|
||||
+ statsProcessFn stats0_;
|
||||
+ /**
|
||||
+ * \brief The function called when a line is ready for statistics processing.
|
||||
+ *
|
||||
+ * Used for line 3 and 4, only needed if the bayer order has 4 different lines.
|
||||
+ */
|
||||
+ statsProcessFn stats2_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief The memory used per pixel in bits.
|
||||
+ */
|
||||
+ unsigned int bpp_;
|
||||
+ /**
|
||||
+ * \brief Skip lines where this bitmask is set in y.
|
||||
+ */
|
||||
+ unsigned int y_skip_mask_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Statistics window, set by setWindow(), used ever line.
|
||||
+ */
|
||||
+ Rectangle window_;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief The function called at the start of a frame.
|
||||
+ */
|
||||
+ statsVoidFn startFrame_;
|
||||
+ /**
|
||||
+ * \brief The function called at the end of a frame.
|
||||
+ */
|
||||
+ statsVoidFn finishFrame_;
|
||||
+ /**
|
||||
+ * \brief The size of the bayer pattern.
|
||||
+ */
|
||||
+ Size patternSize_;
|
||||
+ /**
|
||||
+ * \brief The offset of x, applied to window_.x for bayer variants.
|
||||
+ *
|
||||
+ * This can either be 0 or 1.
|
||||
+ */
|
||||
+ unsigned int x_shift_;
|
||||
+
|
||||
+public:
|
||||
+ /**
|
||||
+ * \brief Get the pattern size.
|
||||
+ *
|
||||
+ * For some input-formats, e.g. Bayer data, processing is done multiple lines
|
||||
+ * and/or columns at a time. Get width and height at which the (bayer) pattern
|
||||
+ * repeats. Window values are rounded down to a multiple of this and the height
|
||||
+ * also indicates if processLine2() should be called or not.
|
||||
+ * This may only be called after a successful configure() call.
|
||||
+ *
|
||||
+ * \return the pattern size.
|
||||
+ */
|
||||
+ const Size &patternSize() { return patternSize_; }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Specify window coordinates over which to gather statistics.
|
||||
+ * \param[in] window The window object.
|
||||
+ */
|
||||
+ void setWindow(Rectangle window)
|
||||
+ {
|
||||
+ window_ = window;
|
||||
+
|
||||
+ window_.x &= ~(patternSize_.width - 1);
|
||||
+ window_.x += x_shift_;
|
||||
+ window_.y &= ~(patternSize_.height - 1);
|
||||
+
|
||||
+ /* width_ - x_shift_ to make sure the window fits */
|
||||
+ window_.width -= x_shift_;
|
||||
+ window_.width &= ~(patternSize_.width - 1);
|
||||
+ window_.height &= ~(patternSize_.height - 1);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Reset state to start statistics gathering for a new frame.
|
||||
+ *
|
||||
+ * This may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void startFrame()
|
||||
+ {
|
||||
+ (this->*startFrame_)();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process line 0.
|
||||
+ * \param[in] y The y coordinate.
|
||||
+ * \param[in] src The input data.
|
||||
+ *
|
||||
+ * This function processes line 0 for input formats with patternSize height == 1.
|
||||
+ * It'll process line 0 and 1 for input formats with patternSize height >= 2.
|
||||
+ * This function may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void processLine0(unsigned int y, const uint8_t *src[])
|
||||
+ {
|
||||
+ if ((y & y_skip_mask_) || y < (unsigned int)window_.y ||
|
||||
+ y >= (window_.y + window_.height))
|
||||
+ return;
|
||||
+
|
||||
+ (this->*stats0_)(src);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process line 2 and 3.
|
||||
+ * \param[in] y The y coordinate.
|
||||
+ * \param[in] src The input data.
|
||||
+ *
|
||||
+ * This function processes line 2 and 3 for input formats with patternSize height == 4.
|
||||
+ * This function may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void processLine2(unsigned int y, const uint8_t *src[])
|
||||
+ {
|
||||
+ if ((y & y_skip_mask_) || y < (unsigned int)window_.y ||
|
||||
+ y >= (window_.y + window_.height))
|
||||
+ return;
|
||||
+
|
||||
+ (this->*stats2_)(src);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Finish statistics calculation for the current frame.
|
||||
+ *
|
||||
+ * This may only be called after a successful setWindow() call.
|
||||
+ */
|
||||
+ void finishFrame()
|
||||
+ {
|
||||
+ (this->*finishFrame_)();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Signals that the statistics are ready.
|
||||
+ *
|
||||
+ * The int parameter isn't actually used.
|
||||
+ */
|
||||
+ Signal<int> statsReady;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
|
||||
index 86494663..3d63e8a2 100644
|
||||
--- a/src/libcamera/meson.build
|
||||
+++ b/src/libcamera/meson.build
|
||||
@@ -71,6 +71,7 @@ subdir('converter')
|
||||
subdir('ipa')
|
||||
subdir('pipeline')
|
||||
subdir('proxy')
|
||||
+subdir('software_isp')
|
||||
|
||||
null_dep = dependency('', required : false)
|
||||
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..9359075d
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -0,0 +1,5 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+libcamera_sources += files([
|
||||
+ 'swstats.cpp',
|
||||
+])
|
||||
diff --git a/src/libcamera/software_isp/swstats.cpp b/src/libcamera/software_isp/swstats.cpp
|
||||
new file mode 100644
|
||||
index 00000000..e65a7ada
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/swstats.cpp
|
||||
@@ -0,0 +1,22 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats.cpp - software statistics base class
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/swstats.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(SwStats)
|
||||
+
|
||||
+SwStats::~SwStats()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From 7eb7164ed7d90ea4cf9ec7e4f35fa8efa25f35e9 Mon Sep 17 00:00:00 2001
|
||||
From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Mon, 11 Dec 2023 17:00:17 +0100
|
||||
Subject: [PATCH 10/25] libcamera: software_isp: Add DebayerCpu class
|
||||
Date: Mon, 11 Mar 2024 15:15:12 +0100
|
||||
Subject: [PATCH 08/21] libcamera: software_isp: Add DebayerCpu class
|
||||
|
||||
Add CPU based debayering implementation. This initial implementation
|
||||
only supports debayering packed 10 bits per pixel bayer data in
|
||||
|
@ -9,191 +9,43 @@ the 4 standard bayer orders.
|
|||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-authored-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-developed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/debayer_cpu.h | 131 +++++
|
||||
.../internal/software_isp/meson.build | 1 +
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 528 ++++++++++++++++++
|
||||
src/libcamera/software_isp/meson.build | 1 +
|
||||
4 files changed, 661 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 626 +++++++++++++++++++++
|
||||
src/libcamera/software_isp/debayer_cpu.h | 143 +++++
|
||||
src/libcamera/software_isp/meson.build | 1 +
|
||||
3 files changed, 770 insertions(+)
|
||||
create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp
|
||||
create mode 100644 src/libcamera/software_isp/debayer_cpu.h
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
new file mode 100644
|
||||
index 00000000..78573f44
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
@@ -0,0 +1,131 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer_cpu.h - CPU based debayering header
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <memory>
|
||||
+#include <stdint.h>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include <libcamera/base/object.h>
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/swstats_cpu.h"
|
||||
+#include "libcamera/internal/software_isp/debayer.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class DebayerCpu
|
||||
+ * \brief Class for debayering on the CPU
|
||||
+ *
|
||||
+ * Implementation for CPU based debayering
|
||||
+ */
|
||||
+class DebayerCpu : public Debayer, public Object
|
||||
+{
|
||||
+public:
|
||||
+ /*
|
||||
+ * FIXME this should be a plain (implementation independent) SwStats
|
||||
+ * this can be fixed once getStats() is dropped.
|
||||
+ */
|
||||
+ /**
|
||||
+ * \brief Constructs a DebayerCpu object.
|
||||
+ * \param[in] stats Pointer to the stats object to use.
|
||||
+ */
|
||||
+ DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
|
||||
+ ~DebayerCpu();
|
||||
+
|
||||
+ /*
|
||||
+ * Setup the Debayer object according to the passed in parameters.
|
||||
+ * Return 0 on success, a negative errno value on failure
|
||||
+ * (unsupported parameters).
|
||||
+ */
|
||||
+ int configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
|
||||
+
|
||||
+ /*
|
||||
+ * Get width and height at which the bayer-pattern repeats.
|
||||
+ * Return pattern-size or an empty Size for an unsupported inputFormat.
|
||||
+ */
|
||||
+ Size patternSize(PixelFormat inputFormat);
|
||||
+
|
||||
+ std::vector<PixelFormat> formats(PixelFormat input);
|
||||
+ std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
||||
+
|
||||
+ void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the file descriptor for the statistics.
|
||||
+ *
|
||||
+ * \return the file descriptor pointing to the statistics.
|
||||
+ */
|
||||
+ const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the output frame size.
|
||||
+ *
|
||||
+ * \return The output frame size.
|
||||
+ */
|
||||
+ unsigned int frameSize() { return outputConfig_.frameSize; }
|
||||
+private:
|
||||
+ void initLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
+ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
+ void process2(const uint8_t *src, uint8_t *dst);
|
||||
+ void process4(const uint8_t *src, uint8_t *dst);
|
||||
+ /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
|
||||
+ void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+
|
||||
+ typedef void (DebayerCpu::*debayerFn)(uint8_t *dst, const uint8_t *src[]);
|
||||
+
|
||||
+ struct DebayerInputConfig {
|
||||
+ Size patternSize;
|
||||
+ unsigned int bpp; /* Memory used per pixel, not precision */
|
||||
+ unsigned int stride;
|
||||
+ std::vector<PixelFormat> outputFormats;
|
||||
+ };
|
||||
+
|
||||
+ struct DebayerOutputConfig {
|
||||
+ unsigned int bpp; /* Memory used per pixel, not precision */
|
||||
+ unsigned int stride;
|
||||
+ unsigned int frameSize;
|
||||
+ };
|
||||
+
|
||||
+ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
|
||||
+ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
|
||||
+ int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
|
||||
+
|
||||
+ uint8_t gamma_[1024];
|
||||
+ uint8_t red_[256];
|
||||
+ uint8_t green_[256];
|
||||
+ uint8_t blue_[256];
|
||||
+ debayerFn debayer0_;
|
||||
+ debayerFn debayer1_;
|
||||
+ debayerFn debayer2_;
|
||||
+ debayerFn debayer3_;
|
||||
+ Rectangle window_;
|
||||
+ DebayerInputConfig inputConfig_;
|
||||
+ DebayerOutputConfig outputConfig_;
|
||||
+ std::unique_ptr<SwStatsCpu> stats_;
|
||||
+ uint8_t *lineBuffers_[5];
|
||||
+ unsigned int lineBufferIndex_;
|
||||
+ bool enableInputMemcpy_;
|
||||
+ float gamma_correction_;
|
||||
+ int measuredFrames_;
|
||||
+ int64_t frameProcessTime_;
|
||||
+ /* Skip 30 frames for things to stabilize then measure 30 frames */
|
||||
+ static const int framesToSkip = 30;
|
||||
+ static const int framesToMeasure = 60;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index 7e40925e..b5a0d737 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
libcamera_internal_headers += files([
|
||||
'debayer.h',
|
||||
+ 'debayer_cpu.h',
|
||||
'debayer_params.h',
|
||||
'swisp_stats.h',
|
||||
'swstats.h',
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
new file mode 100644
|
||||
index 00000000..e0c3c658
|
||||
index 00000000..f932362c
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -0,0 +1,528 @@
|
||||
@@ -0,0 +1,626 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer_cpu.cpp - CPU based debayering class
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/debayer_cpu.h"
|
||||
+#include "debayer_cpu.h"
|
||||
+
|
||||
+#include <math.h>
|
||||
+#include <stdlib.h>
|
||||
|
@ -207,6 +59,17 @@ index 00000000..e0c3c658
|
|||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class DebayerCpu
|
||||
+ * \brief Class for debayering on the CPU
|
||||
+ *
|
||||
+ * Implementation for CPU based debayering
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Constructs a DebayerCpu object.
|
||||
+ * \param[in] stats Pointer to the stats object to use.
|
||||
+ */
|
||||
+DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
|
||||
+ : stats_(std::move(stats)), gamma_correction_(1.0)
|
||||
+{
|
||||
|
@ -216,16 +79,16 @@ index 00000000..e0c3c658
|
|||
+ enableInputMemcpy_ = true;
|
||||
+#endif
|
||||
+ /* Initialize gamma to 1.0 curve */
|
||||
+ for (int i = 0; i < 1024; i++)
|
||||
+ gamma_[i] = i / 4;
|
||||
+ for (unsigned int i = 0; i < kGammaLookupSize; i++)
|
||||
+ gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize);
|
||||
+
|
||||
+ for (int i = 0; i < 5; i++)
|
||||
+ lineBuffers_[i] = NULL;
|
||||
+ for (unsigned int i = 0; i < kMaxLineBuffers; i++)
|
||||
+ lineBuffers_[i] = nullptr;
|
||||
+}
|
||||
+
|
||||
+DebayerCpu::~DebayerCpu()
|
||||
+{
|
||||
+ for (int i = 0; i < 5; i++)
|
||||
+ for (unsigned int i = 0; i < kMaxLineBuffers; i++)
|
||||
+ free(lineBuffers_[i]);
|
||||
+}
|
||||
+
|
||||
|
@ -276,16 +139,17 @@ index 00000000..e0c3c658
|
|||
+ * For the first pixel getting a pixel from the previous column uses
|
||||
+ * x - 2 to skip the 5th byte with least-significant bits for 4 pixels.
|
||||
+ * Same for last pixel (uses x + 2) and looking at the next column.
|
||||
+ * x++ in the for-loop skips the 5th byte with 4 x 2 lsb-s for 10bit packed.
|
||||
+ */
|
||||
+ for (int x = 0; x < width_in_bytes; x++) {
|
||||
+ /* Even pixel */
|
||||
+ for (int x = 0; x < width_in_bytes;) {
|
||||
+ /* First pixel */
|
||||
+ BGGR_BGR888(2, 1, 1)
|
||||
+ /* Odd pixel BGGR -> GBRG */
|
||||
+ /* Second pixel BGGR -> GBRG */
|
||||
+ GBRG_BGR888(1, 1, 1)
|
||||
+ /* Same thing for next 2 pixels */
|
||||
+ /* Same thing for third and fourth pixels */
|
||||
+ BGGR_BGR888(1, 1, 1)
|
||||
+ GBRG_BGR888(1, 2, 1)
|
||||
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
@ -296,14 +160,16 @@ index 00000000..e0c3c658
|
|||
+ const uint8_t *curr = (const uint8_t *)src[1];
|
||||
+ const uint8_t *next = (const uint8_t *)src[2];
|
||||
+
|
||||
+ for (int x = 0; x < width_in_bytes; x++) {
|
||||
+ /* Even pixel */
|
||||
+ for (int x = 0; x < width_in_bytes;) {
|
||||
+ /* First pixel */
|
||||
+ GRBG_BGR888(2, 1, 1)
|
||||
+ /* Odd pixel GRBG -> RGGB */
|
||||
+ /* Second pixel GRBG -> RGGB */
|
||||
+ RGGB_BGR888(1, 1, 1)
|
||||
+ /* Same thing for next 2 pixels */
|
||||
+ /* Same thing for third and fourth pixels */
|
||||
+ GRBG_BGR888(1, 1, 1)
|
||||
+ RGGB_BGR888(1, 2, 1)
|
||||
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
@ -314,7 +180,7 @@ index 00000000..e0c3c658
|
|||
+ const uint8_t *curr = (const uint8_t *)src[1];
|
||||
+ const uint8_t *next = (const uint8_t *)src[2];
|
||||
+
|
||||
+ for (int x = 0; x < width_in_bytes; x++) {
|
||||
+ for (int x = 0; x < width_in_bytes;) {
|
||||
+ /* Even pixel */
|
||||
+ GBRG_BGR888(2, 1, 1)
|
||||
+ /* Odd pixel GBGR -> BGGR */
|
||||
|
@ -322,6 +188,8 @@ index 00000000..e0c3c658
|
|||
+ /* Same thing for next 2 pixels */
|
||||
+ GBRG_BGR888(1, 1, 1)
|
||||
+ BGGR_BGR888(1, 2, 1)
|
||||
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
@ -332,14 +200,16 @@ index 00000000..e0c3c658
|
|||
+ const uint8_t *curr = (const uint8_t *)src[1];
|
||||
+ const uint8_t *next = (const uint8_t *)src[2];
|
||||
+
|
||||
+ for (int x = 0; x < width_in_bytes; x++) {
|
||||
+ for (int x = 0; x < width_in_bytes;) {
|
||||
+ /* Even pixel */
|
||||
+ RGGB_BGR888(2, 1, 1)
|
||||
+ /* Odd pixel RGGB -> GRBG*/
|
||||
+ /* Odd pixel RGGB -> GRBG */
|
||||
+ GRBG_BGR888(1, 1, 1)
|
||||
+ /* Same thing for next 2 pixels */
|
||||
+ RGGB_BGR888(1, 1, 1)
|
||||
+ GRBG_BGR888(1, 2, 1)
|
||||
+ /* Skip 5th src byte with 4 x 2 least-significant-bits */
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
@ -349,6 +219,11 @@ index 00000000..e0c3c658
|
|||
+ order == BayerFormat::GRBG || order == BayerFormat::RGGB;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Setup the Debayer object according to the passed in parameters.
|
||||
+ * Return 0 on success, a negative errno value on failure
|
||||
+ * (unsupported parameters).
|
||||
+ */
|
||||
+int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
|
||||
+{
|
||||
+ BayerFormat bayerFormat =
|
||||
|
@ -468,15 +343,15 @@ index 00000000..e0c3c658
|
|||
+ /* Don't pass x,y since process() already adjusts src before passing it */
|
||||
+ stats_->setWindow(Rectangle(window_.size()));
|
||||
+
|
||||
+ /* pad with patternSize.Width on both left and right side */
|
||||
+ lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
|
||||
+ lineBufferLength_ = window_.width * inputConfig_.bpp / 8 +
|
||||
+ 2 * lineBufferPadding_;
|
||||
+ for (unsigned int i = 0;
|
||||
+ i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_;
|
||||
+ i++) {
|
||||
+ /* pad with patternSize.Width on both left and right side */
|
||||
+ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
|
||||
+ inputConfig_.bpp / 8;
|
||||
+
|
||||
+ free(lineBuffers_[i]);
|
||||
+ lineBuffers_[i] = (uint8_t *)malloc(lineLength);
|
||||
+ lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_);
|
||||
+ if (!lineBuffers_[i])
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
|
@ -487,6 +362,10 @@ index 00000000..e0c3c658
|
|||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Get width and height at which the bayer-pattern repeats.
|
||||
+ * Return pattern-size or an empty Size for an unsupported inputFormat.
|
||||
+ */
|
||||
+Size DebayerCpu::patternSize(PixelFormat inputFormat)
|
||||
+{
|
||||
+ DebayerCpu::DebayerInputConfig config;
|
||||
|
@ -521,25 +400,17 @@ index 00000000..e0c3c658
|
|||
+ return std::make_tuple(stride, stride * size.height);
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::initLinePointers(const uint8_t *linePointers[], const uint8_t *src)
|
||||
+void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[])
|
||||
+{
|
||||
+ const int patternHeight = inputConfig_.patternSize.height;
|
||||
+
|
||||
+ for (int i = 0; i < patternHeight; i++)
|
||||
+ linePointers[i + 1] = src +
|
||||
+ (-patternHeight / 2 + i) * (int)inputConfig_.stride;
|
||||
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
|
||||
+
|
||||
+ if (!enableInputMemcpy_)
|
||||
+ return;
|
||||
+
|
||||
+ for (int i = 0; i < patternHeight; i++) {
|
||||
+ /* pad with patternSize.Width on both left and right side */
|
||||
+ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
|
||||
+ inputConfig_.bpp / 8;
|
||||
+ int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
|
||||
+
|
||||
+ memcpy(lineBuffers_[i], linePointers[i + 1] - padding, lineLength);
|
||||
+ linePointers[i + 1] = lineBuffers_[i] + padding;
|
||||
+ for (unsigned int i = 0; i < patternHeight; i++) {
|
||||
+ memcpy(lineBuffers_[i], linePointers[i + 1] - lineBufferPadding_,
|
||||
+ lineBufferLength_);
|
||||
+ linePointers[i + 1] = lineBuffers_[i] + lineBufferPadding_;
|
||||
+ }
|
||||
+
|
||||
+ /* Point lineBufferIndex_ to first unused lineBuffer */
|
||||
|
@ -548,44 +419,78 @@ index 00000000..e0c3c658
|
|||
+
|
||||
+void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
|
||||
+{
|
||||
+ const int patternHeight = inputConfig_.patternSize.height;
|
||||
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
|
||||
+
|
||||
+ for (int i = 0; i < patternHeight; i++)
|
||||
+ for (unsigned int i = 0; i < patternHeight; i++)
|
||||
+ linePointers[i] = linePointers[i + 1];
|
||||
+
|
||||
+ linePointers[patternHeight] = src +
|
||||
+ (patternHeight / 2) * (int)inputConfig_.stride;
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[])
|
||||
+{
|
||||
+ const unsigned int patternHeight = inputConfig_.patternSize.height;
|
||||
+
|
||||
+ if (!enableInputMemcpy_)
|
||||
+ return;
|
||||
+
|
||||
+ size_t lineLength = (window_.width + 2 * inputConfig_.patternSize.width) *
|
||||
+ inputConfig_.bpp / 8;
|
||||
+ int padding = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
|
||||
+ memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - padding, lineLength);
|
||||
+ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + padding;
|
||||
+ memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - lineBufferPadding_,
|
||||
+ lineBufferLength_);
|
||||
+ linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + lineBufferPadding_;
|
||||
+
|
||||
+ lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
|
||||
+{
|
||||
+ const unsigned int y_end = window_.y + window_.height;
|
||||
+ unsigned int y_end = window_.y + window_.height;
|
||||
+ /* Holds [0] previous- [1] current- [2] next-line */
|
||||
+ const uint8_t *linePointers[3];
|
||||
+
|
||||
+ /* Adjust src to top left corner of the window */
|
||||
+ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
|
||||
+
|
||||
+ initLinePointers(linePointers, src);
|
||||
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
|
||||
+ if (window_.y) {
|
||||
+ linePointers[1] = src - inputConfig_.stride; /* previous-line */
|
||||
+ linePointers[2] = src;
|
||||
+ } else {
|
||||
+ /* window_.y == 0, use the next line as prev line */
|
||||
+ linePointers[1] = src + inputConfig_.stride;
|
||||
+ linePointers[2] = src;
|
||||
+ /* Last 2 lines also need special handling */
|
||||
+ y_end -= 2;
|
||||
+ }
|
||||
+
|
||||
+ setupInputMemcpy(linePointers);
|
||||
+
|
||||
+ for (unsigned int y = window_.y; y < y_end; y += 2) {
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ stats_->processLine0(y, linePointers);
|
||||
+ (this->*debayer0_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ (this->*debayer1_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+ }
|
||||
+
|
||||
+ if (window_.y == 0) {
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ stats_->processLine0(y_end, linePointers);
|
||||
+ (this->*debayer0_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ /* next line may point outside of src, use prev. */
|
||||
+ linePointers[2] = linePointers[0];
|
||||
+ (this->*debayer1_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
|
@ -595,32 +500,46 @@ index 00000000..e0c3c658
|
|||
+void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
|
||||
+{
|
||||
+ const unsigned int y_end = window_.y + window_.height;
|
||||
+ /*
|
||||
+ * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line
|
||||
+ * [3] 1-line-down [4] 2-lines-down.
|
||||
+ */
|
||||
+ const uint8_t *linePointers[5];
|
||||
+
|
||||
+ /* Adjust src to top left corner of the window */
|
||||
+ src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
|
||||
+
|
||||
+ initLinePointers(linePointers, src);
|
||||
+ /* [x] becomes [x - 1] after initial shiftLinePointers() call */
|
||||
+ linePointers[1] = src - 2 * inputConfig_.stride;
|
||||
+ linePointers[2] = src - inputConfig_.stride;
|
||||
+ linePointers[3] = src;
|
||||
+ linePointers[4] = src + inputConfig_.stride;
|
||||
+
|
||||
+ setupInputMemcpy(linePointers);
|
||||
+
|
||||
+ for (unsigned int y = window_.y; y < y_end; y += 4) {
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ stats_->processLine0(y, linePointers);
|
||||
+ (this->*debayer0_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ (this->*debayer1_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ stats_->processLine2(y, linePointers);
|
||||
+ (this->*debayer2_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
+
|
||||
+ shiftLinePointers(linePointers, src);
|
||||
+ memcpyNextLine(linePointers);
|
||||
+ (this->*debayer3_)(dst, linePointers);
|
||||
+ src += inputConfig_.stride;
|
||||
+ dst += outputConfig_.stride;
|
||||
|
@ -637,30 +556,32 @@ index 00000000..e0c3c658
|
|||
+{
|
||||
+ timespec frameStartTime;
|
||||
+
|
||||
+ if (measuredFrames_ < DebayerCpu::framesToMeasure) {
|
||||
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) {
|
||||
+ frameStartTime = {};
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
|
||||
+ }
|
||||
+
|
||||
+ /* Apply DebayerParams */
|
||||
+ if (params.gamma != gamma_correction_) {
|
||||
+ for (int i = 0; i < 1024; i++)
|
||||
+ gamma_[i] = 255 * powf(i / 1023.0, params.gamma);
|
||||
+ for (unsigned int i = 0; i < kGammaLookupSize; i++)
|
||||
+ gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
|
||||
+
|
||||
+ gamma_correction_ = params.gamma;
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < 256; i++) {
|
||||
+ int idx;
|
||||
+ for (unsigned int i = 0; i < kRGBLookupSize; i++) {
|
||||
+ constexpr unsigned int div =
|
||||
+ kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
|
||||
+ unsigned int idx;
|
||||
+
|
||||
+ /* Apply gamma after gain! */
|
||||
+ idx = std::min({ i * params.gainR / 64U, 1023U });
|
||||
+ idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) });
|
||||
+ red_[i] = gamma_[idx];
|
||||
+
|
||||
+ idx = std::min({ i * params.gainG / 64U, 1023U });
|
||||
+ idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) });
|
||||
+ green_[i] = gamma_[idx];
|
||||
+
|
||||
+ idx = std::min({ i * params.gainB / 64U, 1023U });
|
||||
+ idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) });
|
||||
+ blue_[i] = gamma_[idx];
|
||||
+ }
|
||||
+
|
||||
|
@ -688,14 +609,14 @@ index 00000000..e0c3c658
|
|||
+ metadata.planes()[0].bytesused = out.planes()[0].size();
|
||||
+
|
||||
+ /* Measure before emitting signals */
|
||||
+ if (measuredFrames_ < DebayerCpu::framesToMeasure &&
|
||||
+ ++measuredFrames_ > DebayerCpu::framesToSkip) {
|
||||
+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
|
||||
+ ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
|
||||
+ timespec frameEndTime = {};
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
|
||||
+ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
|
||||
+ if (measuredFrames_ == DebayerCpu::framesToMeasure) {
|
||||
+ const int measuredFrames = DebayerCpu::framesToMeasure -
|
||||
+ DebayerCpu::framesToSkip;
|
||||
+ if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
|
||||
+ const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
|
||||
+ DebayerCpu::kFramesToSkip;
|
||||
+ LOG(Debayer, Info)
|
||||
+ << "Processed " << measuredFrames
|
||||
+ << " frames in " << frameProcessTime_ / 1000 << "us, "
|
||||
|
@ -709,19 +630,196 @@ index 00000000..e0c3c658
|
|||
+ inputBufferReady.emit(input);
|
||||
+}
|
||||
+
|
||||
+SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
+{
|
||||
+ Size pattern_size = patternSize(inputFormat);
|
||||
+ unsigned int border_height = pattern_size.height;
|
||||
+
|
||||
+ if (pattern_size.isNull())
|
||||
+ return {};
|
||||
+
|
||||
+ /* No need for top/bottom border with a pattern height of 2 */
|
||||
+ if (pattern_size.height == 2)
|
||||
+ border_height = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * For debayer interpolation a border is kept around the entire image
|
||||
+ * and the minimum output size is pattern-height x pattern-width.
|
||||
+ */
|
||||
+ if (inputSize.width < (3 * pattern_size.width) ||
|
||||
+ inputSize.height < (2 * border_height + pattern_size.height)) {
|
||||
+ LOG(Debayer, Warning)
|
||||
+ << "Input format size too small: " << inputSize.toString();
|
||||
+ return {};
|
||||
+ }
|
||||
+
|
||||
+ return SizeRange(Size(pattern_size.width, pattern_size.height),
|
||||
+ Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
|
||||
+ (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
|
||||
+ pattern_size.width, pattern_size.height);
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
|
||||
new file mode 100644
|
||||
index 00000000..8a51ed85
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.h
|
||||
@@ -0,0 +1,143 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * debayer_cpu.h - CPU based debayering header
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <memory>
|
||||
+#include <stdint.h>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include <libcamera/base/object.h>
|
||||
+
|
||||
+#include "debayer.h"
|
||||
+#include "swstats_cpu.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class DebayerCpu : public Debayer, public Object
|
||||
+{
|
||||
+public:
|
||||
+ DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
|
||||
+ ~DebayerCpu();
|
||||
+
|
||||
+ int configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
|
||||
+ Size patternSize(PixelFormat inputFormat);
|
||||
+ std::vector<PixelFormat> formats(PixelFormat input);
|
||||
+ std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
||||
+ void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params);
|
||||
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the file descriptor for the statistics.
|
||||
+ *
|
||||
+ * \return the file descriptor pointing to the statistics.
|
||||
+ */
|
||||
+ const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the output frame size.
|
||||
+ *
|
||||
+ * \return The output frame size.
|
||||
+ */
|
||||
+ unsigned int frameSize() { return outputConfig_.frameSize; }
|
||||
+
|
||||
+private:
|
||||
+ /**
|
||||
+ * \brief Called to debayer 1 line of Bayer input data to output format
|
||||
+ * \param[out] dst Pointer to the start of the output line to write
|
||||
+ * \param[in] src The input data
|
||||
+ *
|
||||
+ * Input data is an array of (patternSize_.height + 1) src
|
||||
+ * pointers each pointing to a line in the Bayer source. The middle
|
||||
+ * element of the array will point to the actual line being processed.
|
||||
+ * Earlier element(s) will point to the previous line(s) and later
|
||||
+ * element(s) to the next line(s).
|
||||
+ *
|
||||
+ * These functions take an array of src pointers, rather than
|
||||
+ * a single src pointer + a stride for the source, so that when the src
|
||||
+ * is slow uncached memory it can be copied to faster memory before
|
||||
+ * debayering. Debayering a standard 2x2 Bayer pattern requires access
|
||||
+ * to the previous and next src lines for interpolating the missing
|
||||
+ * colors. To allow copying the src lines only once 3 temporary buffers
|
||||
+ * each holding a single line are used, re-using the oldest buffer for
|
||||
+ * the next line and the pointers are swizzled so that:
|
||||
+ * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
|
||||
+ * This way the 3 pointers passed to the debayer functions form
|
||||
+ * a sliding window over the src avoiding the need to copy each
|
||||
+ * line more than once.
|
||||
+ *
|
||||
+ * Similarly for bayer patterns which repeat every 4 lines, 5 src
|
||||
+ * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up
|
||||
+ * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down.
|
||||
+ */
|
||||
+ using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
|
||||
+
|
||||
+ /* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
|
||||
+ void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+
|
||||
+ struct DebayerInputConfig {
|
||||
+ Size patternSize;
|
||||
+ unsigned int bpp; /* Memory used per pixel, not precision */
|
||||
+ unsigned int stride;
|
||||
+ std::vector<PixelFormat> outputFormats;
|
||||
+ };
|
||||
+
|
||||
+ struct DebayerOutputConfig {
|
||||
+ unsigned int bpp; /* Memory used per pixel, not precision */
|
||||
+ unsigned int stride;
|
||||
+ unsigned int frameSize;
|
||||
+ };
|
||||
+
|
||||
+ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
|
||||
+ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
|
||||
+ int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
|
||||
+ void setupInputMemcpy(const uint8_t *linePointers[]);
|
||||
+ void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
+ void memcpyNextLine(const uint8_t *linePointers[]);
|
||||
+ void process2(const uint8_t *src, uint8_t *dst);
|
||||
+ void process4(const uint8_t *src, uint8_t *dst);
|
||||
+
|
||||
+ static constexpr unsigned int kGammaLookupSize = 1024;
|
||||
+ static constexpr unsigned int kRGBLookupSize = 256;
|
||||
+ /* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
|
||||
+ static constexpr unsigned int kMaxLineBuffers = 5;
|
||||
+
|
||||
+ std::array<uint8_t, kGammaLookupSize> gamma_;
|
||||
+ std::array<uint8_t, kRGBLookupSize> red_;
|
||||
+ std::array<uint8_t, kRGBLookupSize> green_;
|
||||
+ std::array<uint8_t, kRGBLookupSize> blue_;
|
||||
+ debayerFn debayer0_;
|
||||
+ debayerFn debayer1_;
|
||||
+ debayerFn debayer2_;
|
||||
+ debayerFn debayer3_;
|
||||
+ Rectangle window_;
|
||||
+ DebayerInputConfig inputConfig_;
|
||||
+ DebayerOutputConfig outputConfig_;
|
||||
+ std::unique_ptr<SwStatsCpu> stats_;
|
||||
+ uint8_t *lineBuffers_[kMaxLineBuffers];
|
||||
+ unsigned int lineBufferLength_;
|
||||
+ unsigned int lineBufferPadding_;
|
||||
+ unsigned int lineBufferIndex_;
|
||||
+ bool enableInputMemcpy_;
|
||||
+ float gamma_correction_;
|
||||
+ unsigned int measuredFrames_;
|
||||
+ int64_t frameProcessTime_;
|
||||
+ /* Skip 30 frames for things to stabilize then measure 30 frames */
|
||||
+ static constexpr unsigned int kFramesToSkip = 30;
|
||||
+ static constexpr unsigned int kLastFrameToMeasure = 60;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
index d4ae5ac7..6d7a44d7 100644
|
||||
index 62095f61..71b46539 100644
|
||||
--- a/src/libcamera/software_isp/meson.build
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -2,6 +2,7 @@
|
||||
@@ -9,5 +9,6 @@ endif
|
||||
|
||||
libcamera_sources += files([
|
||||
'debayer.cpp',
|
||||
+ 'debayer_cpu.cpp',
|
||||
'swstats.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
'debayer.cpp',
|
||||
+ 'debayer_cpu.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
])
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
From c1c43445cd4408010e500fe9d6b6424c77bcf75d Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Fri, 8 Dec 2023 12:50:57 +0100
|
||||
Subject: [PATCH 08/25] libcamera: software_isp: Add SwStatsCpu class
|
||||
|
||||
Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
|
||||
|
||||
Co-authored-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-authored-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Pavel Machek <pavel@ucw.cz>
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
.../internal/software_isp/meson.build | 1 +
|
||||
.../internal/software_isp/swstats_cpu.h | 44 +++++
|
||||
src/libcamera/software_isp/meson.build | 1 +
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 164 ++++++++++++++++++
|
||||
4 files changed, 210 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index 1c43acc4..1d9e4018 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -3,4 +3,5 @@
|
||||
libcamera_internal_headers += files([
|
||||
'swisp_stats.h',
|
||||
'swstats.h',
|
||||
+ 'swstats_cpu.h',
|
||||
])
|
||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
new file mode 100644
|
||||
index 00000000..8bb86e98
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
@@ -0,0 +1,44 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats_cpu.h - CPU based software statistics implementation
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include "libcamera/internal/shared_mem_object.h"
|
||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
+#include "libcamera/internal/software_isp/swstats.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \class SwStatsCpu
|
||||
+ * \brief Implementation for the Software statistics on the CPU.
|
||||
+ */
|
||||
+class SwStatsCpu : public SwStats
|
||||
+{
|
||||
+public:
|
||||
+ SwStatsCpu();
|
||||
+ ~SwStatsCpu() { }
|
||||
+
|
||||
+ bool isValid() const { return sharedStats_.fd().isValid(); }
|
||||
+ const SharedFD &getStatsFD() { return sharedStats_.fd(); }
|
||||
+ int configure(const StreamConfiguration &inputCfg);
|
||||
+private:
|
||||
+ void statsBGGR10PLine0(const uint8_t *src[]);
|
||||
+ void statsGBRG10PLine0(const uint8_t *src[]);
|
||||
+ void resetStats(void);
|
||||
+ void finishStats(void);
|
||||
+
|
||||
+ SharedMemObject<SwIspStats> sharedStats_;
|
||||
+ SwIspStats stats_;
|
||||
+ bool swap_lines_;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
index 9359075d..d31c6217 100644
|
||||
--- a/src/libcamera/software_isp/meson.build
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
libcamera_sources += files([
|
||||
'swstats.cpp',
|
||||
+ 'swstats_cpu.cpp',
|
||||
])
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
new file mode 100644
|
||||
index 00000000..59453d07
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
@@ -0,0 +1,164 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ * Copyright (C) 2023, Red Hat Inc.
|
||||
+ *
|
||||
+ * Authors:
|
||||
+ * Hans de Goede <hdegoede@redhat.com>
|
||||
+ *
|
||||
+ * swstats_cpu.cpp - CPU based software statistics implementation
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/swstats_cpu.h"
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+#include <libcamera/stream.h>
|
||||
+
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+SwStatsCpu::SwStatsCpu()
|
||||
+ : SwStats()
|
||||
+{
|
||||
+ sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
|
||||
+ if (!sharedStats_.fd().isValid())
|
||||
+ LOG(SwStats, Error)
|
||||
+ << "Failed to create shared memory for statistics";
|
||||
+}
|
||||
+
|
||||
+/* for brightness values in the 0 to 255 range: */
|
||||
+static const unsigned int BRIGHT_LVL = 200U << 8;
|
||||
+static const unsigned int TOO_BRIGHT_LVL = 240U << 8;
|
||||
+
|
||||
+static const unsigned int RED_Y_MUL = 77; /* 0.30 * 256 */
|
||||
+static const unsigned int GREEN_Y_MUL = 150; /* 0.59 * 256 */
|
||||
+static const unsigned int BLUE_Y_MUL = 29; /* 0.11 * 256 */
|
||||
+
|
||||
+#define SWISP_LINARO_START_LINE_STATS(pixel_t) \
|
||||
+ pixel_t r, g, g2, b; \
|
||||
+ unsigned int y_val; \
|
||||
+ \
|
||||
+ unsigned int sumR = 0; \
|
||||
+ unsigned int sumG = 0; \
|
||||
+ unsigned int sumB = 0;
|
||||
+
|
||||
+#define SWISP_LINARO_ACCUMULATE_LINE_STATS(div) \
|
||||
+ sumR += r; \
|
||||
+ sumG += g; \
|
||||
+ sumB += b; \
|
||||
+ \
|
||||
+ y_val = r * RED_Y_MUL; \
|
||||
+ y_val += g * GREEN_Y_MUL; \
|
||||
+ y_val += b * BLUE_Y_MUL; \
|
||||
+ stats_.y_histogram[y_val / (256 * 16 * (div))]++;
|
||||
+
|
||||
+#define SWISP_LINARO_FINISH_LINE_STATS() \
|
||||
+ stats_.sumR_ += sumR; \
|
||||
+ stats_.sumG_ += sumG; \
|
||||
+ stats_.sumB_ += sumB;
|
||||
+
|
||||
+static inline __attribute__((always_inline)) void
|
||||
+statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, SwIspStats &stats_)
|
||||
+{
|
||||
+ const int width_in_bytes = width * 5 / 4;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint8_t)
|
||||
+
|
||||
+ for (int x = 0; x < width_in_bytes; x += 5) {
|
||||
+ if (bggr) {
|
||||
+ /* BGGR */
|
||||
+ b = src0[x];
|
||||
+ g = src0[x + 1];
|
||||
+ g2 = src1[x];
|
||||
+ r = src1[x + 1];
|
||||
+ } else {
|
||||
+ /* GBRG */
|
||||
+ g = src0[x];
|
||||
+ b = src0[x + 1];
|
||||
+ r = src1[x];
|
||||
+ g2 = src1[x + 1];
|
||||
+ }
|
||||
+ g = (g + g2) / 2;
|
||||
+
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(1)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
|
||||
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
|
||||
+
|
||||
+ if (swap_lines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ statsBayer10P(window_.width, src0, src1, true, stats_);
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint8_t *src0 = src[1] + window_.x * 5 / 4;
|
||||
+ const uint8_t *src1 = src[2] + window_.x * 5 / 4;
|
||||
+
|
||||
+ if (swap_lines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ statsBayer10P(window_.width, src0, src1, false, stats_);
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::resetStats(void)
|
||||
+{
|
||||
+ stats_.sumR_ = 0;
|
||||
+ stats_.sumB_ = 0;
|
||||
+ stats_.sumG_ = 0;
|
||||
+ std::fill_n(stats_.y_histogram, 16, 0);
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::finishStats(void)
|
||||
+{
|
||||
+ *sharedStats_ = stats_;
|
||||
+ statsReady.emit(0);
|
||||
+}
|
||||
+
|
||||
+int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
+{
|
||||
+ BayerFormat bayerFormat =
|
||||
+ BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
|
||||
+
|
||||
+ startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
|
||||
+ finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
|
||||
+
|
||||
+ if (bayerFormat.bitDepth == 10 &&
|
||||
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
|
||||
+ bpp_ = 10;
|
||||
+ patternSize_.height = 2;
|
||||
+ patternSize_.width = 4; /* 5 bytes per *4* pixels */
|
||||
+ y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */
|
||||
+ x_shift_ = 0;
|
||||
+
|
||||
+ switch (bayerFormat.order) {
|
||||
+ case BayerFormat::BGGR:
|
||||
+ case BayerFormat::GRBG:
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10PLine0;
|
||||
+ swap_lines_ = bayerFormat.order == BayerFormat::GRBG;
|
||||
+ return 0;
|
||||
+ case BayerFormat::GBRG:
|
||||
+ case BayerFormat::RGGB:
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsGBRG10PLine0;
|
||||
+ swap_lines_ = bayerFormat.order == BayerFormat::RGGB;
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOG(SwStats, Info)
|
||||
+ << "Unsupported input format " << inputCfg.pixelFormat.toString();
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -0,0 +1,506 @@
|
|||
From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Mon, 11 Mar 2024 15:15:13 +0100
|
||||
Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA
|
||||
|
||||
Define the Soft IPA main and event interfaces, add the Soft IPA
|
||||
implementation.
|
||||
|
||||
The current src/ipa/meson.build assumes the IPA name to match the
|
||||
pipeline name. For this reason "-Dipas=simple" is used for the
|
||||
Soft IPA module.
|
||||
|
||||
Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
|
||||
|
||||
Auto exposure/gain targets a Mean Sample Value of 2.5 following
|
||||
the MSV calculation algorithm from:
|
||||
https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
|
||||
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-developed-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Co-developed-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
Documentation/Doxyfile.in | 1 +
|
||||
include/libcamera/ipa/meson.build | 1 +
|
||||
include/libcamera/ipa/soft.mojom | 28 +++
|
||||
meson_options.txt | 2 +-
|
||||
src/ipa/simple/data/meson.build | 9 +
|
||||
src/ipa/simple/data/soft.conf | 3 +
|
||||
src/ipa/simple/meson.build | 25 +++
|
||||
src/ipa/simple/soft_simple.cpp | 326 ++++++++++++++++++++++++++++++
|
||||
8 files changed, 394 insertions(+), 1 deletion(-)
|
||||
create mode 100644 include/libcamera/ipa/soft.mojom
|
||||
create mode 100644 src/ipa/simple/data/meson.build
|
||||
create mode 100644 src/ipa/simple/data/soft.conf
|
||||
create mode 100644 src/ipa/simple/meson.build
|
||||
create mode 100644 src/ipa/simple/soft_simple.cpp
|
||||
|
||||
diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
|
||||
index a86ea6c1..2be8d47b 100644
|
||||
--- a/Documentation/Doxyfile.in
|
||||
+++ b/Documentation/Doxyfile.in
|
||||
@@ -44,6 +44,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \
|
||||
@TOP_SRCDIR@/src/libcamera/pipeline/ \
|
||||
@TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
|
||||
@TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
|
||||
+ @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
|
||||
@TOP_BUILDDIR@/src/libcamera/proxy/
|
||||
|
||||
EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
|
||||
diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
|
||||
index f3b4881c..3352d08f 100644
|
||||
--- a/include/libcamera/ipa/meson.build
|
||||
+++ b/include/libcamera/ipa/meson.build
|
||||
@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
|
||||
'ipu3': 'ipu3.mojom',
|
||||
'rkisp1': 'rkisp1.mojom',
|
||||
'rpi/vc4': 'raspberrypi.mojom',
|
||||
+ 'simple': 'soft.mojom',
|
||||
'vimc': 'vimc.mojom',
|
||||
}
|
||||
|
||||
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
|
||||
new file mode 100644
|
||||
index 00000000..c249bd75
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/ipa/soft.mojom
|
||||
@@ -0,0 +1,28 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+
|
||||
+/*
|
||||
+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
|
||||
+ */
|
||||
+
|
||||
+module ipa.soft;
|
||||
+
|
||||
+import "include/libcamera/ipa/core.mojom";
|
||||
+
|
||||
+interface IPASoftInterface {
|
||||
+ init(libcamera.IPASettings settings,
|
||||
+ libcamera.SharedFD fdStats,
|
||||
+ libcamera.SharedFD fdParams,
|
||||
+ libcamera.ControlInfoMap sensorCtrlInfoMap)
|
||||
+ => (int32 ret);
|
||||
+ start() => (int32 ret);
|
||||
+ stop();
|
||||
+ configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
|
||||
+ => (int32 ret);
|
||||
+
|
||||
+ [async] processStats(libcamera.ControlList sensorControls);
|
||||
+};
|
||||
+
|
||||
+interface IPASoftEventInterface {
|
||||
+ setSensorControls(libcamera.ControlList sensorControls);
|
||||
+ setIspParams(int32 dummy);
|
||||
+};
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
index 5fdc7be8..94372e47 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
@@ -27,7 +27,7 @@ option('gstreamer',
|
||||
|
||||
option('ipas',
|
||||
type : 'array',
|
||||
- choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
|
||||
+ choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],
|
||||
description : 'Select which IPA modules to build')
|
||||
|
||||
option('lc-compliance',
|
||||
diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..33548cc6
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/data/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+conf_files = files([
|
||||
+ 'soft.conf',
|
||||
+])
|
||||
+
|
||||
+install_data(conf_files,
|
||||
+ install_dir : ipa_data_dir / 'soft',
|
||||
+ install_tag : 'runtime')
|
||||
diff --git a/src/ipa/simple/data/soft.conf b/src/ipa/simple/data/soft.conf
|
||||
new file mode 100644
|
||||
index 00000000..0c70e7c0
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/data/soft.conf
|
||||
@@ -0,0 +1,3 @@
|
||||
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
+#
|
||||
+# Dummy configuration file for the soft IPA.
|
||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..3e863db7
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/meson.build
|
||||
@@ -0,0 +1,25 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+ipa_name = 'ipa_soft_simple'
|
||||
+
|
||||
+mod = shared_module(ipa_name,
|
||||
+ ['soft_simple.cpp', libcamera_generated_ipa_headers],
|
||||
+ name_prefix : '',
|
||||
+ include_directories : [ipa_includes, libipa_includes],
|
||||
+ dependencies : libcamera_private,
|
||||
+ link_with : libipa,
|
||||
+ install : true,
|
||||
+ install_dir : ipa_install_dir)
|
||||
+
|
||||
+if ipa_sign_module
|
||||
+ custom_target(ipa_name + '.so.sign',
|
||||
+ input : mod,
|
||||
+ output : ipa_name + '.so.sign',
|
||||
+ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
|
||||
+ install : false,
|
||||
+ build_by_default : true)
|
||||
+endif
|
||||
+
|
||||
+subdir('data')
|
||||
+
|
||||
+ipa_names += ipa_name
|
||||
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
|
||||
new file mode 100644
|
||||
index 00000000..312df4ba
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/soft_simple.cpp
|
||||
@@ -0,0 +1,326 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * soft_simple.cpp - Simple Software Image Processing Algorithm module
|
||||
+ */
|
||||
+
|
||||
+#include <sys/mman.h>
|
||||
+
|
||||
+#include <libcamera/base/file.h>
|
||||
+#include <libcamera/base/log.h>
|
||||
+#include <libcamera/base/shared_fd.h>
|
||||
+
|
||||
+#include <libcamera/control_ids.h>
|
||||
+#include <libcamera/controls.h>
|
||||
+
|
||||
+#include <libcamera/ipa/ipa_interface.h>
|
||||
+#include <libcamera/ipa/ipa_module_info.h>
|
||||
+#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
+
|
||||
+#include "libcamera/internal/camera_sensor.h"
|
||||
+#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(IPASoft)
|
||||
+
|
||||
+namespace ipa::soft {
|
||||
+
|
||||
+class IPASoftSimple : public ipa::soft::IPASoftInterface
|
||||
+{
|
||||
+public:
|
||||
+ IPASoftSimple()
|
||||
+ : params_(static_cast<DebayerParams *>(MAP_FAILED)),
|
||||
+ stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
|
||||
+ {
|
||||
+ }
|
||||
+
|
||||
+ ~IPASoftSimple()
|
||||
+ {
|
||||
+ if (stats_ != MAP_FAILED)
|
||||
+ munmap(stats_, sizeof(SwIspStats));
|
||||
+ if (params_ != MAP_FAILED)
|
||||
+ munmap(params_, sizeof(DebayerParams));
|
||||
+ }
|
||||
+
|
||||
+ int init(const IPASettings &settings,
|
||||
+ const SharedFD &fdStats,
|
||||
+ const SharedFD &fdParams,
|
||||
+ const ControlInfoMap &sensorInfoMap) override;
|
||||
+ int configure(const ControlInfoMap &sensorInfoMap) override;
|
||||
+
|
||||
+ int start() override;
|
||||
+ void stop() override;
|
||||
+
|
||||
+ void processStats(const ControlList &sensorControls) override;
|
||||
+
|
||||
+private:
|
||||
+ void updateExposure(double exposureMSV);
|
||||
+
|
||||
+ SharedFD fdStats_;
|
||||
+ SharedFD fdParams_;
|
||||
+ DebayerParams *params_;
|
||||
+ SwIspStats *stats_;
|
||||
+
|
||||
+ int32_t exposure_min_, exposure_max_;
|
||||
+ int32_t again_min_, again_max_;
|
||||
+ int32_t again_, exposure_;
|
||||
+ unsigned int ignore_updates_;
|
||||
+};
|
||||
+
|
||||
+int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
|
||||
+ const SharedFD &fdStats,
|
||||
+ const SharedFD &fdParams,
|
||||
+ const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ fdStats_ = fdStats;
|
||||
+ if (!fdStats_.isValid()) {
|
||||
+ LOG(IPASoft, Error) << "Invalid Statistics handle";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ fdParams_ = fdParams;
|
||||
+ if (!fdParams_.isValid()) {
|
||||
+ LOG(IPASoft, Error) << "Invalid Parameters handle";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
|
||||
+ PROT_WRITE, MAP_SHARED,
|
||||
+ fdParams_.get(), 0));
|
||||
+ if (params_ == MAP_FAILED) {
|
||||
+ LOG(IPASoft, Error) << "Unable to map Parameters";
|
||||
+ return -errno;
|
||||
+ }
|
||||
+
|
||||
+ stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
|
||||
+ PROT_READ, MAP_SHARED,
|
||||
+ fdStats_.get(), 0));
|
||||
+ if (stats_ == MAP_FAILED) {
|
||||
+ LOG(IPASoft, Error) << "Unable to map Statistics";
|
||||
+ return -errno;
|
||||
+ }
|
||||
+
|
||||
+ if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {
|
||||
+ LOG(IPASoft, Error) << "Don't have exposure control";
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {
|
||||
+ LOG(IPASoft, Error) << "Don't have gain control";
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second;
|
||||
+ const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second;
|
||||
+
|
||||
+ exposure_min_ = exposure_info.min().get<int32_t>();
|
||||
+ exposure_max_ = exposure_info.max().get<int32_t>();
|
||||
+ if (!exposure_min_) {
|
||||
+ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
|
||||
+ exposure_min_ = 1;
|
||||
+ }
|
||||
+
|
||||
+ again_min_ = gain_info.min().get<int32_t>();
|
||||
+ again_max_ = gain_info.max().get<int32_t>();
|
||||
+ /*
|
||||
+ * The camera sensor gain (g) is usually not equal to the value written
|
||||
+ * into the gain register (x). But the way how the AGC algorithm changes
|
||||
+ * the gain value to make the total exposure closer to the optimum assumes
|
||||
+ * that g(x) is not too far from linear function. If the minimal gain is 0,
|
||||
+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
|
||||
+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
|
||||
+ * one edge, and very small near the other) we limit the range of the gain
|
||||
+ * values used.
|
||||
+ */
|
||||
+ if (!again_min_) {
|
||||
+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
|
||||
+ again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
|
||||
+ }
|
||||
+
|
||||
+ LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
|
||||
+ << ", gain " << again_min_ << "-" << again_max_;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int IPASoftSimple::start()
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void IPASoftSimple::stop()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * The number of bins to use for the optimal exposure calculations.
|
||||
+ */
|
||||
+static constexpr unsigned int kExposureBinsCount = 5;
|
||||
+/*
|
||||
+ * The exposure is optimal when the mean sample value of the histogram is
|
||||
+ * in the middle of the range.
|
||||
+ */
|
||||
+static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;
|
||||
+/*
|
||||
+ * The below value implements the hysteresis for the exposure adjustment.
|
||||
+ * It is small enough to have the exposure close to the optimal, and is big
|
||||
+ * enough to prevent the exposure from wobbling around the optimal value.
|
||||
+ */
|
||||
+static constexpr float kExposureSatisfactory = 0.2;
|
||||
+
|
||||
+void IPASoftSimple::processStats(const ControlList &sensorControls)
|
||||
+{
|
||||
+ /*
|
||||
+ * Calculate red and blue gains for AWB.
|
||||
+ * Clamp max gain at 4.0, this also avoids 0 division.
|
||||
+ */
|
||||
+ if (stats_->sumR_ <= stats_->sumG_ / 4)
|
||||
+ params_->gainR = 1024;
|
||||
+ else
|
||||
+ params_->gainR = 256 * stats_->sumG_ / stats_->sumR_;
|
||||
+
|
||||
+ if (stats_->sumB_ <= stats_->sumG_ / 4)
|
||||
+ params_->gainB = 1024;
|
||||
+ else
|
||||
+ params_->gainB = 256 * stats_->sumG_ / stats_->sumB_;
|
||||
+
|
||||
+ /* Green gain and gamma values are fixed */
|
||||
+ params_->gainG = 256;
|
||||
+ params_->gamma = 0.5;
|
||||
+
|
||||
+ setIspParams.emit(0);
|
||||
+
|
||||
+ /*
|
||||
+ * AE / AGC, use 2 frames delay to make sure that the exposure and
|
||||
+ * the gain set have applied to the camera sensor.
|
||||
+ */
|
||||
+ if (ignore_updates_ > 0) {
|
||||
+ --ignore_updates_;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Calculate Mean Sample Value (MSV) according to formula from:
|
||||
+ * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
|
||||
+ */
|
||||
+ constexpr unsigned int yHistValsPerBin =
|
||||
+ SwIspStats::kYHistogramSize / kExposureBinsCount;
|
||||
+ constexpr unsigned int yHistValsPerBinMod =
|
||||
+ SwIspStats::kYHistogramSize /
|
||||
+ (SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
|
||||
+ int ExposureBins[kExposureBinsCount] = {};
|
||||
+ unsigned int denom = 0;
|
||||
+ unsigned int num = 0;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
|
||||
+ unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
|
||||
+ ExposureBins[idx] += stats_->yHistogram[i];
|
||||
+ }
|
||||
+
|
||||
+ for (unsigned int i = 0; i < kExposureBinsCount; i++) {
|
||||
+ LOG(IPASoft, Debug) << i << ": " << ExposureBins[i];
|
||||
+ denom += ExposureBins[i];
|
||||
+ num += ExposureBins[i] * (i + 1);
|
||||
+ }
|
||||
+
|
||||
+ float exposureMSV = (float)num / denom;
|
||||
+
|
||||
+ /* sanity check */
|
||||
+ if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
|
||||
+ !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
|
||||
+ LOG(IPASoft, Error) << "Control(s) missing";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ControlList ctrls(sensorControls);
|
||||
+
|
||||
+ exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
|
||||
+ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
|
||||
+
|
||||
+ updateExposure(exposureMSV);
|
||||
+
|
||||
+ ctrls.set(V4L2_CID_EXPOSURE, exposure_);
|
||||
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
|
||||
+
|
||||
+ ignore_updates_ = 2;
|
||||
+
|
||||
+ setSensorControls.emit(ctrls);
|
||||
+
|
||||
+ LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
|
||||
+ << " exp " << exposure_ << " again " << again_
|
||||
+ << " gain R/B " << params_->gainR << "/" << params_->gainB;
|
||||
+}
|
||||
+
|
||||
+void IPASoftSimple::updateExposure(double exposureMSV)
|
||||
+{
|
||||
+ /* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
|
||||
+ static constexpr uint8_t kExpDenominator = 10;
|
||||
+ static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
|
||||
+ static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
|
||||
+
|
||||
+ int next;
|
||||
+
|
||||
+ if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
|
||||
+ next = exposure_ * kExpNumeratorUp / kExpDenominator;
|
||||
+ if (next - exposure_ < 1)
|
||||
+ exposure_ += 1;
|
||||
+ else
|
||||
+ exposure_ = next;
|
||||
+ if (exposure_ >= exposure_max_) {
|
||||
+ next = again_ * kExpNumeratorUp / kExpDenominator;
|
||||
+ if (next - again_ < 1)
|
||||
+ again_ += 1;
|
||||
+ else
|
||||
+ again_ = next;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
|
||||
+ if (exposure_ == exposure_max_ && again_ != again_min_) {
|
||||
+ next = again_ * kExpNumeratorDown / kExpDenominator;
|
||||
+ if (again_ - next < 1)
|
||||
+ again_ -= 1;
|
||||
+ else
|
||||
+ again_ = next;
|
||||
+ } else {
|
||||
+ next = exposure_ * kExpNumeratorDown / kExpDenominator;
|
||||
+ if (exposure_ - next < 1)
|
||||
+ exposure_ -= 1;
|
||||
+ else
|
||||
+ exposure_ = next;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_);
|
||||
+ again_ = std::clamp(again_, again_min_, again_max_);
|
||||
+}
|
||||
+
|
||||
+} /* namespace ipa::soft */
|
||||
+
|
||||
+/*
|
||||
+ * External IPA module interface
|
||||
+ */
|
||||
+extern "C" {
|
||||
+const struct IPAModuleInfo ipaModuleInfo = {
|
||||
+ IPA_MODULE_API_VERSION,
|
||||
+ 0,
|
||||
+ "SimplePipelineHandler",
|
||||
+ "simple",
|
||||
+};
|
||||
+
|
||||
+IPAInterface *ipaCreate()
|
||||
+{
|
||||
+ return new ipa::soft::IPASoftSimple();
|
||||
+}
|
||||
+
|
||||
+} /* extern "C" */
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -0,0 +1,507 @@
|
|||
From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Mon, 11 Mar 2024 15:15:14 +0100
|
||||
Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp
|
||||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/meson.build | 1 +
|
||||
.../internal/software_isp/software_isp.h | 98 +++++
|
||||
src/libcamera/software_isp/meson.build | 1 +
|
||||
src/libcamera/software_isp/software_isp.cpp | 349 ++++++++++++++++++
|
||||
4 files changed, 449 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/software_isp.h
|
||||
create mode 100644 src/libcamera/software_isp/software_isp.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index a620e16d..508ddddc 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
libcamera_internal_headers += files([
|
||||
'debayer_params.h',
|
||||
+ 'software_isp.h',
|
||||
'swisp_stats.h',
|
||||
])
|
||||
diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
|
||||
new file mode 100644
|
||||
index 00000000..8d25e979
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/software_isp.h
|
||||
@@ -0,0 +1,98 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * software_isp.h - Simple software ISP implementation
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <functional>
|
||||
+#include <initializer_list>
|
||||
+#include <map>
|
||||
+#include <memory>
|
||||
+#include <string>
|
||||
+#include <tuple>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include <libcamera/base/class.h>
|
||||
+#include <libcamera/base/log.h>
|
||||
+#include <libcamera/base/signal.h>
|
||||
+#include <libcamera/base/thread.h>
|
||||
+
|
||||
+#include <libcamera/geometry.h>
|
||||
+#include <libcamera/pixel_format.h>
|
||||
+
|
||||
+#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
+#include <libcamera/ipa/soft_ipa_proxy.h>
|
||||
+
|
||||
+#include "libcamera/internal/dma_heaps.h"
|
||||
+#include "libcamera/internal/pipeline_handler.h"
|
||||
+#include "libcamera/internal/shared_mem_object.h"
|
||||
+#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class DebayerCpu;
|
||||
+class FrameBuffer;
|
||||
+class PixelFormat;
|
||||
+struct StreamConfiguration;
|
||||
+
|
||||
+LOG_DECLARE_CATEGORY(SoftwareIsp)
|
||||
+
|
||||
+class SoftwareIsp
|
||||
+{
|
||||
+public:
|
||||
+ SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
|
||||
+ ~SoftwareIsp();
|
||||
+
|
||||
+ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
|
||||
+
|
||||
+ bool isValid() const;
|
||||
+
|
||||
+ std::vector<PixelFormat> formats(PixelFormat input);
|
||||
+
|
||||
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
|
||||
+
|
||||
+ std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
||||
+
|
||||
+ int configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
+ const ControlInfoMap &sensorControls);
|
||||
+
|
||||
+ int exportBuffers(unsigned int output, unsigned int count,
|
||||
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers);
|
||||
+
|
||||
+ void processStats(const ControlList &sensorControls);
|
||||
+
|
||||
+ int start();
|
||||
+ void stop();
|
||||
+
|
||||
+ int queueBuffers(FrameBuffer *input,
|
||||
+ const std::map<unsigned int, FrameBuffer *> &outputs);
|
||||
+
|
||||
+ void process(FrameBuffer *input, FrameBuffer *output);
|
||||
+
|
||||
+ Signal<FrameBuffer *> inputBufferReady;
|
||||
+ Signal<FrameBuffer *> outputBufferReady;
|
||||
+ Signal<int> ispStatsReady;
|
||||
+ Signal<const ControlList &> setSensorControls;
|
||||
+
|
||||
+private:
|
||||
+ void saveIspParams(int dummy);
|
||||
+ void setSensorCtrls(const ControlList &sensorControls);
|
||||
+ void statsReady(int dummy);
|
||||
+ void inputReady(FrameBuffer *input);
|
||||
+ void outputReady(FrameBuffer *output);
|
||||
+
|
||||
+ std::unique_ptr<DebayerCpu> debayer_;
|
||||
+ Thread ispWorkerThread_;
|
||||
+ SharedMemObject<DebayerParams> sharedParams_;
|
||||
+ DebayerParams debayerParams_;
|
||||
+ DmaHeap dmaHeap_;
|
||||
+
|
||||
+ std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
index 71b46539..e9266e54 100644
|
||||
--- a/src/libcamera/software_isp/meson.build
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -10,5 +10,6 @@ endif
|
||||
libcamera_sources += files([
|
||||
'debayer.cpp',
|
||||
'debayer_cpu.cpp',
|
||||
+ 'software_isp.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
])
|
||||
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
|
||||
new file mode 100644
|
||||
index 00000000..388b4496
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/software_isp.cpp
|
||||
@@ -0,0 +1,349 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * software_isp.cpp - Simple software ISP implementation
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/software_isp.h"
|
||||
+
|
||||
+#include <sys/mman.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include <libcamera/formats.h>
|
||||
+#include <libcamera/stream.h>
|
||||
+
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
+#include "libcamera/internal/framebuffer.h"
|
||||
+#include "libcamera/internal/ipa_manager.h"
|
||||
+#include "libcamera/internal/mapped_framebuffer.h"
|
||||
+
|
||||
+#include "debayer_cpu.h"
|
||||
+
|
||||
+/**
|
||||
+ * \file software_isp.cpp
|
||||
+ * \brief Simple software ISP implementation
|
||||
+ */
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(SoftwareIsp)
|
||||
+
|
||||
+/**
|
||||
+ * \class SoftwareIsp
|
||||
+ * \brief Class for the Software ISP
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \var SoftwareIsp::inputBufferReady
|
||||
+ * \brief A signal emitted when the input frame buffer completes
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \var SoftwareIsp::outputBufferReady
|
||||
+ * \brief A signal emitted when the output frame buffer completes
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \var SoftwareIsp::ispStatsReady
|
||||
+ * \brief A signal emitted when the statistics for IPA are ready
|
||||
+ *
|
||||
+ * The int parameter isn't actually used.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \var SoftwareIsp::setSensorControls
|
||||
+ * \brief A signal emitted when the values to write to the sensor controls are ready
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Constructs SoftwareIsp object
|
||||
+ * \param[in] pipe The pipeline handler in use
|
||||
+ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
|
||||
+ */
|
||||
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
|
||||
+ : debayer_(nullptr),
|
||||
+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
|
||||
+ dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
||||
+{
|
||||
+ if (!dmaHeap_.isValid()) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
|
||||
+ if (!sharedParams_) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ auto stats = std::make_unique<SwStatsCpu>();
|
||||
+ if (!stats->isValid()) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
|
||||
+ return;
|
||||
+ }
|
||||
+ stats->statsReady.connect(this, &SoftwareIsp::statsReady);
|
||||
+
|
||||
+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
|
||||
+ debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
|
||||
+ debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
|
||||
+
|
||||
+ ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
|
||||
+ if (!ipa_) {
|
||||
+ LOG(SoftwareIsp, Error)
|
||||
+ << "Creating IPA for software ISP failed";
|
||||
+ debayer_.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
|
||||
+ debayer_->getStatsFD(),
|
||||
+ sharedParams_.fd(),
|
||||
+ sensorControls);
|
||||
+ if (ret) {
|
||||
+ LOG(SoftwareIsp, Error) << "IPA init failed";
|
||||
+ debayer_.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
|
||||
+ ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
|
||||
+
|
||||
+ debayer_->moveToThread(&ispWorkerThread_);
|
||||
+}
|
||||
+
|
||||
+SoftwareIsp::~SoftwareIsp()
|
||||
+{
|
||||
+ /* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
|
||||
+ debayer_.reset();
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
|
||||
+ * \brief Load a configuration from a file
|
||||
+ * \param[in] filename The file to load the configuration data from
|
||||
+ *
|
||||
+ * Currently is a stub doing nothing and always returning "success".
|
||||
+ *
|
||||
+ * \return 0 on success
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * \brief Process the statistics gathered
|
||||
+ * \param[in] sensorControls The sensor controls
|
||||
+ *
|
||||
+ * Requests the IPA to calculate new parameters for ISP and new control
|
||||
+ * values for the sensor.
|
||||
+ */
|
||||
+void SoftwareIsp::processStats(const ControlList &sensorControls)
|
||||
+{
|
||||
+ ASSERT(ipa_);
|
||||
+ ipa_->processStats(sensorControls);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Check the validity of Software Isp object
|
||||
+ * \return True if Software Isp is valid, false otherwise
|
||||
+ */
|
||||
+bool SoftwareIsp::isValid() const
|
||||
+{
|
||||
+ return !!debayer_;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Get the output formats supported for the given input format
|
||||
+ * \param[in] inputFormat The input format
|
||||
+ * \return All the supported output formats or an empty vector if there are none
|
||||
+ */
|
||||
+std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->formats(inputFormat);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Get the supported output sizes for the given input format and size
|
||||
+ * \param[in] inputFormat The input format
|
||||
+ * \param[in] inputSize The input frame size
|
||||
+ * \return The valid size range or an empty range if there are none
|
||||
+ */
|
||||
+SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->sizes(inputFormat, inputSize);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Get the output stride and the frame size in bytes for the given output format and size
|
||||
+ * \param[in] outputFormat The output format
|
||||
+ * \param[in] size The output size (width and height in pixels)
|
||||
+ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
|
||||
+ * if there is no valid output config
|
||||
+ */
|
||||
+std::tuple<unsigned int, unsigned int>
|
||||
+SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->strideAndFrameSize(outputFormat, size);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Configure the SoftwareIsp object according to the passed in parameters
|
||||
+ * \param[in] inputCfg The input configuration
|
||||
+ * \param[in] outputCfgs The output configurations
|
||||
+ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
|
||||
+ * \return 0 on success, a negative errno on failure
|
||||
+ */
|
||||
+int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
+ const ControlInfoMap &sensorControls)
|
||||
+{
|
||||
+ ASSERT(ipa_ != nullptr && debayer_ != nullptr);
|
||||
+
|
||||
+ int ret = ipa_->configure(sensorControls);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return debayer_->configure(inputCfg, outputCfgs);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Export the buffers from the Software ISP
|
||||
+ * \param[in] output Output stream index exporting the buffers
|
||||
+ * \param[in] count Number of buffers to allocate
|
||||
+ * \param[out] buffers Vector to store the allocated buffers
|
||||
+ * \return The number of allocated buffers on success or a negative error code
|
||||
+ * otherwise
|
||||
+ */
|
||||
+int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
|
||||
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ /* single output for now */
|
||||
+ if (output >= 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < count; i++) {
|
||||
+ const std::string name = "frame-" + std::to_string(i);
|
||||
+ const size_t frameSize = debayer_->frameSize();
|
||||
+
|
||||
+ FrameBuffer::Plane outPlane;
|
||||
+ outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
|
||||
+ if (!outPlane.fd.isValid()) {
|
||||
+ LOG(SoftwareIsp, Error)
|
||||
+ << "failed to allocate a dma_buf";
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ outPlane.offset = 0;
|
||||
+ outPlane.length = frameSize;
|
||||
+
|
||||
+ std::vector<FrameBuffer::Plane> planes{ outPlane };
|
||||
+ buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
|
||||
+ }
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Queue buffers to Software ISP
|
||||
+ * \param[in] input The input framebuffer
|
||||
+ * \param[in] outputs The container holding the output stream indexes and
|
||||
+ * their respective frame buffer outputs
|
||||
+ * \return 0 on success, a negative errno on failure
|
||||
+ */
|
||||
+int SoftwareIsp::queueBuffers(FrameBuffer *input,
|
||||
+ const std::map<unsigned int, FrameBuffer *> &outputs)
|
||||
+{
|
||||
+ unsigned int mask = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Validate the outputs as a sanity check: at least one output is
|
||||
+ * required, all outputs must reference a valid stream and no two
|
||||
+ * outputs can reference the same stream.
|
||||
+ */
|
||||
+ if (outputs.empty())
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (auto [index, buffer] : outputs) {
|
||||
+ if (!buffer)
|
||||
+ return -EINVAL;
|
||||
+ if (index >= 1) /* only single stream atm */
|
||||
+ return -EINVAL;
|
||||
+ if (mask & (1 << index))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mask |= 1 << index;
|
||||
+ }
|
||||
+
|
||||
+ process(input, outputs.at(0));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Starts the Software ISP streaming operation
|
||||
+ * \return 0 on success, any other value indicates an error
|
||||
+ */
|
||||
+int SoftwareIsp::start()
|
||||
+{
|
||||
+ int ret = ipa_->start();
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ispWorkerThread_.start();
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Stops the Software ISP streaming operation
|
||||
+ */
|
||||
+void SoftwareIsp::stop()
|
||||
+{
|
||||
+ ispWorkerThread_.exit();
|
||||
+ ispWorkerThread_.wait();
|
||||
+
|
||||
+ ipa_->stop();
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Passes the input framebuffer to the ISP worker to process
|
||||
+ * \param[in] input The input framebuffer
|
||||
+ * \param[out] output The framebuffer to write the processed frame to
|
||||
+ */
|
||||
+void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
|
||||
+{
|
||||
+ debayer_->invokeMethod(&DebayerCpu::process,
|
||||
+ ConnectionTypeQueued, input, output, debayerParams_);
|
||||
+}
|
||||
+
|
||||
+void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy)
|
||||
+{
|
||||
+ debayerParams_ = *sharedParams_;
|
||||
+}
|
||||
+
|
||||
+void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
|
||||
+{
|
||||
+ setSensorControls.emit(sensorControls);
|
||||
+}
|
||||
+
|
||||
+void SoftwareIsp::statsReady(int dummy)
|
||||
+{
|
||||
+ ispStatsReady.emit(dummy);
|
||||
+}
|
||||
+
|
||||
+void SoftwareIsp::inputReady(FrameBuffer *input)
|
||||
+{
|
||||
+ inputBufferReady.emit(input);
|
||||
+}
|
||||
+
|
||||
+void SoftwareIsp::outputReady(FrameBuffer *output)
|
||||
+{
|
||||
+ outputBufferReady.emit(output);
|
||||
+}
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
From 05b353f1e45f2af0d0989261210b4bedef5144de Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Mon, 11 Dec 2023 23:41:58 +0300
|
||||
Subject: [PATCH 11/25] libcamera: ipa: add Soft IPA common files
|
||||
|
||||
Define the Soft IPA main and event interfaces, add IPASoftBase
|
||||
class the Soft IPA implementation inherit from.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
Documentation/Doxyfile.in | 1 +
|
||||
include/libcamera/ipa/meson.build | 1 +
|
||||
include/libcamera/ipa/soft.mojom | 29 ++++++++++++
|
||||
src/ipa/simple/common/meson.build | 17 +++++++
|
||||
src/ipa/simple/common/soft_base.cpp | 73 +++++++++++++++++++++++++++++
|
||||
src/ipa/simple/common/soft_base.h | 50 ++++++++++++++++++++
|
||||
src/ipa/simple/meson.build | 3 ++
|
||||
7 files changed, 174 insertions(+)
|
||||
create mode 100644 include/libcamera/ipa/soft.mojom
|
||||
create mode 100644 src/ipa/simple/common/meson.build
|
||||
create mode 100644 src/ipa/simple/common/soft_base.cpp
|
||||
create mode 100644 src/ipa/simple/common/soft_base.h
|
||||
create mode 100644 src/ipa/simple/meson.build
|
||||
|
||||
diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
|
||||
index a86ea6c1..2be8d47b 100644
|
||||
--- a/Documentation/Doxyfile.in
|
||||
+++ b/Documentation/Doxyfile.in
|
||||
@@ -44,6 +44,7 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/base/span.h \
|
||||
@TOP_SRCDIR@/src/libcamera/pipeline/ \
|
||||
@TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
|
||||
@TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
|
||||
+ @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
|
||||
@TOP_BUILDDIR@/src/libcamera/proxy/
|
||||
|
||||
EXCLUDE_PATTERNS = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
|
||||
diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
|
||||
index f3b4881c..894e38a6 100644
|
||||
--- a/include/libcamera/ipa/meson.build
|
||||
+++ b/include/libcamera/ipa/meson.build
|
||||
@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
|
||||
'ipu3': 'ipu3.mojom',
|
||||
'rkisp1': 'rkisp1.mojom',
|
||||
'rpi/vc4': 'raspberrypi.mojom',
|
||||
+ 'simple/simple': 'soft.mojom',
|
||||
'vimc': 'vimc.mojom',
|
||||
}
|
||||
|
||||
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
|
||||
new file mode 100644
|
||||
index 00000000..2dae652b
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/ipa/soft.mojom
|
||||
@@ -0,0 +1,29 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+
|
||||
+/*
|
||||
+ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
|
||||
+ * \todo Add a way to tell SoftIPA the list of params SoftISP accepts?
|
||||
+ */
|
||||
+
|
||||
+module ipa.soft;
|
||||
+
|
||||
+import "include/libcamera/ipa/core.mojom";
|
||||
+
|
||||
+interface IPASoftInterface {
|
||||
+ init(libcamera.IPASettings settings,
|
||||
+ libcamera.SharedFD fdStats,
|
||||
+ libcamera.SharedFD fdParams,
|
||||
+ libcamera.ControlInfoMap sensorCtrlInfoMap)
|
||||
+ => (int32 ret);
|
||||
+ start() => (int32 ret);
|
||||
+ stop();
|
||||
+ configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
|
||||
+ => (int32 ret);
|
||||
+
|
||||
+ [async] processStats(libcamera.ControlList sensorControls);
|
||||
+};
|
||||
+
|
||||
+interface IPASoftEventInterface {
|
||||
+ setSensorControls(libcamera.ControlList sensorControls);
|
||||
+ setIspParams(int32 dummy);
|
||||
+};
|
||||
diff --git a/src/ipa/simple/common/meson.build b/src/ipa/simple/common/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..023e617b
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/common/meson.build
|
||||
@@ -0,0 +1,17 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+soft_ipa_common_sources = files([
|
||||
+ 'soft_base.cpp',
|
||||
+])
|
||||
+
|
||||
+soft_ipa_common_includes = [
|
||||
+ include_directories('..'),
|
||||
+]
|
||||
+
|
||||
+soft_ipa_common_deps = [
|
||||
+ libcamera_private,
|
||||
+]
|
||||
+
|
||||
+soft_ipa_common_lib = static_library('soft_ipa_common', soft_ipa_common_sources,
|
||||
+ include_directories : soft_ipa_common_includes,
|
||||
+ dependencies : soft_ipa_common_deps)
|
||||
diff --git a/src/ipa/simple/common/soft_base.cpp b/src/ipa/simple/common/soft_base.cpp
|
||||
new file mode 100644
|
||||
index 00000000..b4ed9023
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/common/soft_base.cpp
|
||||
@@ -0,0 +1,73 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * soft-base.cpp - Software IPA base class
|
||||
+ */
|
||||
+
|
||||
+#include "soft_base.h"
|
||||
+
|
||||
+#include <sys/mman.h>
|
||||
+
|
||||
+#include <libcamera/base/file.h>
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+#include <libcamera/control_ids.h>
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(IPASoft)
|
||||
+
|
||||
+namespace ipa::soft {
|
||||
+
|
||||
+IPASoftBase::IPASoftBase()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+IPASoftBase::~IPASoftBase()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+int IPASoftBase::init([[maybe_unused]] const IPASettings &settings,
|
||||
+ const SharedFD &fdStats,
|
||||
+ const SharedFD &fdParams,
|
||||
+ const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ fdStats_ = std::move(fdStats);
|
||||
+ if (!fdStats_.isValid()) {
|
||||
+ LOG(IPASoft, Error) << "Invalid Statistics handle";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ fdParams_ = std::move(fdParams);
|
||||
+ if (!fdParams_.isValid()) {
|
||||
+ LOG(IPASoft, Error) << "Invalid Parameters handle";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ return platformInit(sensorInfoMap);
|
||||
+}
|
||||
+
|
||||
+int IPASoftBase::configure(const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ return platformConfigure(sensorInfoMap);
|
||||
+}
|
||||
+
|
||||
+int IPASoftBase::start()
|
||||
+{
|
||||
+ return platformStart();
|
||||
+}
|
||||
+
|
||||
+void IPASoftBase::stop()
|
||||
+{
|
||||
+ return platformStop();
|
||||
+}
|
||||
+
|
||||
+void IPASoftBase::processStats(const ControlList &sensorControls)
|
||||
+{
|
||||
+ return platformProcessStats(sensorControls);
|
||||
+}
|
||||
+
|
||||
+} /* namespace ipa::soft */
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/ipa/simple/common/soft_base.h b/src/ipa/simple/common/soft_base.h
|
||||
new file mode 100644
|
||||
index 00000000..85c98702
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/common/soft_base.h
|
||||
@@ -0,0 +1,50 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * soft-base.h - Software IPA base class
|
||||
+ */
|
||||
+#pragma once
|
||||
+
|
||||
+#include <libcamera/base/shared_fd.h>
|
||||
+
|
||||
+#include <libcamera/controls.h>
|
||||
+
|
||||
+#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+namespace ipa::soft {
|
||||
+
|
||||
+class IPASoftBase : public ipa::soft::IPASoftInterface
|
||||
+{
|
||||
+public:
|
||||
+ IPASoftBase();
|
||||
+ ~IPASoftBase();
|
||||
+
|
||||
+ int init(const IPASettings &settings,
|
||||
+ const SharedFD &fdStats,
|
||||
+ const SharedFD &fdParams,
|
||||
+ const ControlInfoMap &sensorInfoMap) override;
|
||||
+ int configure(const ControlInfoMap &sensorInfoMap) override;
|
||||
+
|
||||
+ int start() override;
|
||||
+ void stop() override;
|
||||
+
|
||||
+ void processStats(const ControlList &sensorControls) override;
|
||||
+
|
||||
+protected:
|
||||
+ SharedFD fdStats_;
|
||||
+ SharedFD fdParams_;
|
||||
+
|
||||
+private:
|
||||
+ virtual int platformInit(const ControlInfoMap &sensorInfoMap) = 0;
|
||||
+ virtual int platformConfigure(const ControlInfoMap &sensorInfoMap) = 0;
|
||||
+ virtual int platformStart() = 0;
|
||||
+ virtual void platformStop() = 0;
|
||||
+ virtual void platformProcessStats(const ControlList &sensorControls) = 0;
|
||||
+};
|
||||
+
|
||||
+} /* namespace ipa::soft */
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..9688bbdb
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/meson.build
|
||||
@@ -0,0 +1,3 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+subdir('common')
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From 155065b3d78c14173fd71c21fe0639f94c3da109 Mon Sep 17 00:00:00 2001
|
||||
From 050440eed6ab90686df217f5ff7dea0b241e3898 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Fri, 1 Dec 2023 15:45:14 +0300
|
||||
Subject: [PATCH 14/25] libcamera: pipeline: simple: rename converterBuffers_
|
||||
Date: Mon, 11 Mar 2024 15:15:15 +0100
|
||||
Subject: [PATCH 11/21] libcamera: pipeline: simple: rename converterBuffers_
|
||||
and related vars
|
||||
|
||||
The converterBuffers_ and the converterQueue_ are not that specific
|
||||
|
@ -12,16 +12,18 @@ Rename converterBuffers_, converterQueue_, and useConverter_ to
|
|||
conversionBuffers_, conversionQueue_ and useConversion_ to
|
||||
disassociate them from the Converter.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------
|
||||
1 file changed, 32 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
|
||||
index 4d0e7255..fa298746 100644
|
||||
index a84f6760..78854ef8 100644
|
||||
--- a/src/libcamera/pipeline/simple/simple.cpp
|
||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
|
||||
@@ -269,17 +269,18 @@ public:
|
||||
|
@ -135,7 +137,7 @@ index 4d0e7255..fa298746 100644
|
|||
{
|
||||
SimplePipelineHandler *pipe = SimpleCameraData::pipe();
|
||||
|
||||
@@ -1159,14 +1160,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
|
||||
@@ -1189,14 +1190,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
|
||||
|
||||
/* Configure the converter if needed. */
|
||||
std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
|
||||
|
@ -152,7 +154,7 @@ index 4d0e7255..fa298746 100644
|
|||
outputCfgs.push_back(cfg);
|
||||
}
|
||||
|
||||
@@ -1192,7 +1193,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
|
||||
@@ -1222,7 +1223,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
|
||||
* Export buffers on the converter or capture video node, depending on
|
||||
* whether the converter is used or not.
|
||||
*/
|
||||
|
@ -161,7 +163,7 @@ index 4d0e7255..fa298746 100644
|
|||
return data->converter_->exportBuffers(data->streamIndex(stream),
|
||||
count, buffers);
|
||||
else
|
||||
@@ -1213,13 +1214,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
@@ -1243,13 +1244,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -177,7 +179,7 @@ index 4d0e7255..fa298746 100644
|
|||
} else {
|
||||
/* Otherwise, prepare for using buffers from the only stream. */
|
||||
Stream *stream = &data->streams_[0];
|
||||
@@ -1238,7 +1239,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
@@ -1268,7 +1269,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -186,7 +188,7 @@ index 4d0e7255..fa298746 100644
|
|||
ret = data->converter_->start();
|
||||
if (ret < 0) {
|
||||
stop(camera);
|
||||
@@ -1246,7 +1247,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
@@ -1276,7 +1277,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
}
|
||||
|
||||
/* Queue all internal buffers for capture. */
|
||||
|
@ -195,7 +197,7 @@ index 4d0e7255..fa298746 100644
|
|||
video->queueBuffer(buffer.get());
|
||||
}
|
||||
|
||||
@@ -1258,7 +1259,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
@@ -1288,7 +1289,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
SimpleCameraData *data = cameraData(camera);
|
||||
V4L2VideoDevice *video = data->video_;
|
||||
|
||||
|
@ -204,7 +206,7 @@ index 4d0e7255..fa298746 100644
|
|||
data->converter_->stop();
|
||||
|
||||
video->streamOff();
|
||||
@@ -1266,7 +1267,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
@@ -1296,7 +1297,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
|
||||
video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
|
||||
|
||||
|
@ -213,7 +215,7 @@ index 4d0e7255..fa298746 100644
|
|||
|
||||
releasePipeline(data);
|
||||
}
|
||||
@@ -1284,7 +1285,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
|
||||
@@ -1314,7 +1315,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
|
||||
* queue, it will be handed to the converter in the capture
|
||||
* completion handler.
|
||||
*/
|
||||
|
@ -222,7 +224,7 @@ index 4d0e7255..fa298746 100644
|
|||
buffers.emplace(data->streamIndex(stream), buffer);
|
||||
} else {
|
||||
ret = data->video_->queueBuffer(buffer);
|
||||
@@ -1293,8 +1294,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
|
||||
@@ -1323,8 +1324,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,5 +236,5 @@ index 4d0e7255..fa298746 100644
|
|||
return 0;
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
From c0886381a2bbe494b900d699a3858573316059b2 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Mon, 11 Dec 2023 23:47:47 +0300
|
||||
Subject: [PATCH 12/25] libcamera: ipa: Soft IPA: add a Simple Soft IPA
|
||||
implementation
|
||||
|
||||
Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
meson_options.txt | 3 +-
|
||||
src/ipa/simple/meson.build | 9 +
|
||||
src/ipa/simple/simple/data/meson.build | 9 +
|
||||
src/ipa/simple/simple/data/soft.conf | 3 +
|
||||
src/ipa/simple/simple/meson.build | 26 +++
|
||||
src/ipa/simple/simple/soft_simple.cpp | 273 +++++++++++++++++++++++++
|
||||
6 files changed, 322 insertions(+), 1 deletion(-)
|
||||
create mode 100644 src/ipa/simple/simple/data/meson.build
|
||||
create mode 100644 src/ipa/simple/simple/data/soft.conf
|
||||
create mode 100644 src/ipa/simple/simple/meson.build
|
||||
create mode 100644 src/ipa/simple/simple/soft_simple.cpp
|
||||
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
index 5fdc7be8..8ec08658 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
@@ -27,7 +27,7 @@ option('gstreamer',
|
||||
|
||||
option('ipas',
|
||||
type : 'array',
|
||||
- choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
|
||||
+ choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple/simple', 'vimc'],
|
||||
description : 'Select which IPA modules to build')
|
||||
|
||||
option('lc-compliance',
|
||||
@@ -46,6 +46,7 @@ option('pipelines',
|
||||
'rkisp1',
|
||||
'rpi/vc4',
|
||||
'simple',
|
||||
+ 'simple/simple',
|
||||
'uvcvideo',
|
||||
'vimc'
|
||||
],
|
||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
|
||||
index 9688bbdb..14be5dc2 100644
|
||||
--- a/src/ipa/simple/meson.build
|
||||
+++ b/src/ipa/simple/meson.build
|
||||
@@ -1,3 +1,12 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
subdir('common')
|
||||
+
|
||||
+foreach pipeline : pipelines
|
||||
+ pipeline = pipeline.split('/')
|
||||
+ if pipeline.length() < 2 or pipeline[0] != 'simple'
|
||||
+ continue
|
||||
+ endif
|
||||
+
|
||||
+ subdir(pipeline[1])
|
||||
+endforeach
|
||||
diff --git a/src/ipa/simple/simple/data/meson.build b/src/ipa/simple/simple/data/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..33548cc6
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/simple/data/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+conf_files = files([
|
||||
+ 'soft.conf',
|
||||
+])
|
||||
+
|
||||
+install_data(conf_files,
|
||||
+ install_dir : ipa_data_dir / 'soft',
|
||||
+ install_tag : 'runtime')
|
||||
diff --git a/src/ipa/simple/simple/data/soft.conf b/src/ipa/simple/simple/data/soft.conf
|
||||
new file mode 100644
|
||||
index 00000000..0c70e7c0
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/simple/data/soft.conf
|
||||
@@ -0,0 +1,3 @@
|
||||
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
+#
|
||||
+# Dummy configuration file for the soft IPA.
|
||||
diff --git a/src/ipa/simple/simple/meson.build b/src/ipa/simple/simple/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..8b5d76b5
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/simple/meson.build
|
||||
@@ -0,0 +1,26 @@
|
||||
+# SPDX-License-Identifier: CC0-1.0
|
||||
+
|
||||
+ipa_name = 'ipa_soft_simple'
|
||||
+
|
||||
+mod = shared_module(ipa_name,
|
||||
+ ['soft_simple.cpp', libcamera_generated_ipa_headers],
|
||||
+ name_prefix : '',
|
||||
+ include_directories : [ipa_includes, libipa_includes, '..'],
|
||||
+ dependencies : libcamera_private,
|
||||
+ link_with : libipa,
|
||||
+ link_whole : soft_ipa_common_lib,
|
||||
+ install : true,
|
||||
+ install_dir : ipa_install_dir)
|
||||
+
|
||||
+if ipa_sign_module
|
||||
+ custom_target(ipa_name + '.so.sign',
|
||||
+ input : mod,
|
||||
+ output : ipa_name + '.so.sign',
|
||||
+ command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
|
||||
+ install : false,
|
||||
+ build_by_default : true)
|
||||
+endif
|
||||
+
|
||||
+subdir('data')
|
||||
+
|
||||
+ipa_names += ipa_name
|
||||
diff --git a/src/ipa/simple/simple/soft_simple.cpp b/src/ipa/simple/simple/soft_simple.cpp
|
||||
new file mode 100644
|
||||
index 00000000..93fc1545
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/simple/soft_simple.cpp
|
||||
@@ -0,0 +1,273 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * soft_simple.cpp - Simple Software Image Processing Algorithm module
|
||||
+ */
|
||||
+
|
||||
+#include <sys/mman.h>
|
||||
+
|
||||
+#include <libcamera/base/file.h>
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+#include <libcamera/control_ids.h>
|
||||
+
|
||||
+#include <libcamera/ipa/ipa_interface.h>
|
||||
+#include <libcamera/ipa/ipa_module_info.h>
|
||||
+
|
||||
+#include "libcamera/internal/camera_sensor.h"
|
||||
+#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
+
|
||||
+#include "common/soft_base.h"
|
||||
+
|
||||
+#define EXPOSURE_OPTIMAL_VALUE 2.5
|
||||
+#define EXPOSURE_SATISFACTORY_OFFSET 0.2
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DECLARE_CATEGORY(IPASoft)
|
||||
+
|
||||
+namespace ipa::soft {
|
||||
+
|
||||
+class IPASoftSimple final : public IPASoftBase
|
||||
+{
|
||||
+public:
|
||||
+ IPASoftSimple()
|
||||
+ : IPASoftBase(), ignore_updates_(0)
|
||||
+ {
|
||||
+ }
|
||||
+
|
||||
+ ~IPASoftSimple()
|
||||
+ {
|
||||
+ if (stats_)
|
||||
+ munmap(stats_, sizeof(SwIspStats));
|
||||
+ if (params_)
|
||||
+ munmap(params_, sizeof(DebayerParams));
|
||||
+ }
|
||||
+
|
||||
+ int platformInit(const ControlInfoMap &sensorInfoMap) override;
|
||||
+ int platformConfigure(const ControlInfoMap &sensorInfoMap) override;
|
||||
+ int platformStart() override;
|
||||
+ void platformStop() override;
|
||||
+ void platformProcessStats(const ControlList &sensorControls) override;
|
||||
+
|
||||
+private:
|
||||
+ void update_exposure(double exposuremsv);
|
||||
+
|
||||
+ DebayerParams *params_;
|
||||
+ SwIspStats *stats_;
|
||||
+ int exposure_min_, exposure_max_;
|
||||
+ int again_min_, again_max_;
|
||||
+ int again_, exposure_;
|
||||
+ int ignore_updates_;
|
||||
+};
|
||||
+
|
||||
+int IPASoftSimple::platformInit(const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
|
||||
+ PROT_WRITE, MAP_SHARED,
|
||||
+ fdParams_.get(), 0));
|
||||
+ if (!params_) {
|
||||
+ LOG(IPASoft, Error) << "Unable to map Parameters";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
|
||||
+ PROT_READ, MAP_SHARED,
|
||||
+ fdStats_.get(), 0));
|
||||
+ if (!stats_) {
|
||||
+ LOG(IPASoft, Error) << "Unable to map Statistics";
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {
|
||||
+ LOG(IPASoft, Error) << "Don't have exposure control";
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {
|
||||
+ LOG(IPASoft, Error) << "Don't have gain control";
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int IPASoftSimple::platformConfigure(const ControlInfoMap &sensorInfoMap)
|
||||
+{
|
||||
+ const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second;
|
||||
+ const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second;
|
||||
+
|
||||
+ exposure_min_ = exposure_info.min().get<int>();
|
||||
+ if (!exposure_min_) {
|
||||
+ LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
|
||||
+ exposure_min_ = 1;
|
||||
+ }
|
||||
+ exposure_max_ = exposure_info.max().get<int>();
|
||||
+ again_min_ = gain_info.min().get<int>();
|
||||
+ if (!again_min_) {
|
||||
+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
|
||||
+ again_min_ = 100;
|
||||
+ }
|
||||
+ again_max_ = gain_info.max().get<int>();
|
||||
+
|
||||
+ LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
|
||||
+ << ", gain " << again_min_ << "-" << again_max_;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int IPASoftSimple::platformStart()
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void IPASoftSimple::platformStop()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+void IPASoftSimple::platformProcessStats(const ControlList &sensorControls)
|
||||
+{
|
||||
+ /*
|
||||
+ * Calculate red and blue gains for AWB.
|
||||
+ * Clamp max gain at 4.0, this also avoids 0 division.
|
||||
+ */
|
||||
+ if (stats_->sumR_ <= stats_->sumG_ / 4)
|
||||
+ params_->gainR = 1024;
|
||||
+ else
|
||||
+ params_->gainR = 256 * stats_->sumG_ / stats_->sumR_;
|
||||
+
|
||||
+ if (stats_->sumB_ <= stats_->sumG_ / 4)
|
||||
+ params_->gainB = 1024;
|
||||
+ else
|
||||
+ params_->gainB = 256 * stats_->sumG_ / stats_->sumB_;
|
||||
+
|
||||
+ /* Green gain and gamma values are fixed */
|
||||
+ params_->gainG = 256;
|
||||
+ params_->gamma = 0.5;
|
||||
+
|
||||
+ setIspParams.emit(0);
|
||||
+
|
||||
+ /*
|
||||
+ * AE / AGC, use 2 frames delay to make sure that the exposure and
|
||||
+ * the gain set have applied to the camera sensor.
|
||||
+ */
|
||||
+ if (ignore_updates_ > 0) {
|
||||
+ --ignore_updates_;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ unsigned int denom = 0;
|
||||
+ unsigned int num = 0;
|
||||
+ unsigned int y_histogramSmall[5] = {};
|
||||
+
|
||||
+ for (int i = 0; i < 16; i++)
|
||||
+ y_histogramSmall[(i - i / 8) / 3] += stats_->y_histogram[i];
|
||||
+
|
||||
+ for (int i = 0; i < 5; i++) {
|
||||
+ LOG(IPASoft, Debug) << i << ": " << y_histogramSmall[i];
|
||||
+ denom += y_histogramSmall[i];
|
||||
+ num += y_histogramSmall[i] * (i + 1);
|
||||
+ }
|
||||
+
|
||||
+ float exposuremsv = (float)num / denom;
|
||||
+
|
||||
+ /* sanity check */
|
||||
+ if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
|
||||
+ !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
|
||||
+ LOG(IPASoft, Error) << "Control(s) missing";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ControlList ctrls(sensorControls);
|
||||
+
|
||||
+ exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int>();
|
||||
+ again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int>();
|
||||
+
|
||||
+ update_exposure(exposuremsv);
|
||||
+
|
||||
+ ctrls.set(V4L2_CID_EXPOSURE, exposure_);
|
||||
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
|
||||
+
|
||||
+ ignore_updates_ = 2;
|
||||
+
|
||||
+ setSensorControls.emit(ctrls);
|
||||
+
|
||||
+ LOG(IPASoft, Debug) << "exposuremsv " << exposuremsv
|
||||
+ << " exp " << exposure_ << " again " << again_
|
||||
+ << " gain R/B " << params_->gainR << "/" << params_->gainB;
|
||||
+}
|
||||
+
|
||||
+/* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
|
||||
+#define DENOMINATOR 10
|
||||
+#define UP_NUMERATOR (DENOMINATOR + 1)
|
||||
+#define DOWN_NUMERATOR (DENOMINATOR - 1)
|
||||
+
|
||||
+void IPASoftSimple::update_exposure(double exposuremsv)
|
||||
+{
|
||||
+ int next;
|
||||
+
|
||||
+ if (exposuremsv < EXPOSURE_OPTIMAL_VALUE - EXPOSURE_SATISFACTORY_OFFSET) {
|
||||
+ next = exposure_ * UP_NUMERATOR / DENOMINATOR;
|
||||
+ if (next - exposure_ < 1)
|
||||
+ exposure_ += 1;
|
||||
+ else
|
||||
+ exposure_ = next;
|
||||
+ if (exposure_ >= exposure_max_) {
|
||||
+ next = again_ * UP_NUMERATOR / DENOMINATOR;
|
||||
+ if (next - again_ < 1)
|
||||
+ again_ += 1;
|
||||
+ else
|
||||
+ again_ = next;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (exposuremsv > EXPOSURE_OPTIMAL_VALUE + EXPOSURE_SATISFACTORY_OFFSET) {
|
||||
+ if (exposure_ == exposure_max_ && again_ != again_min_) {
|
||||
+ next = again_ * DOWN_NUMERATOR / DENOMINATOR;
|
||||
+ if (again_ - next < 1)
|
||||
+ again_ -= 1;
|
||||
+ else
|
||||
+ again_ = next;
|
||||
+ } else {
|
||||
+ next = exposure_ * DOWN_NUMERATOR / DENOMINATOR;
|
||||
+ if (exposure_ - next < 1)
|
||||
+ exposure_ -= 1;
|
||||
+ else
|
||||
+ exposure_ = next;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (exposure_ > exposure_max_)
|
||||
+ exposure_ = exposure_max_;
|
||||
+ else if (exposure_ < exposure_min_)
|
||||
+ exposure_ = exposure_min_;
|
||||
+
|
||||
+ if (again_ > again_max_)
|
||||
+ again_ = again_max_;
|
||||
+ else if (again_ < again_min_)
|
||||
+ again_ = again_min_;
|
||||
+}
|
||||
+
|
||||
+} /* namespace ipa::soft */
|
||||
+
|
||||
+/*
|
||||
+ * External IPA module interface
|
||||
+ */
|
||||
+extern "C" {
|
||||
+const struct IPAModuleInfo ipaModuleInfo = {
|
||||
+ IPA_MODULE_API_VERSION,
|
||||
+ 0,
|
||||
+ "SimplePipelineHandler",
|
||||
+ "soft/simple",
|
||||
+};
|
||||
+
|
||||
+IPAInterface *ipaCreate()
|
||||
+{
|
||||
+ return new ipa::soft::IPASoftSimple();
|
||||
+}
|
||||
+
|
||||
+} /* extern "C" */
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,41 +1,70 @@
|
|||
From ad61052d9aea1f1af6aaef9ce7e5d447c595187c Mon Sep 17 00:00:00 2001
|
||||
From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Tue, 12 Dec 2023 02:02:37 +0300
|
||||
Subject: [PATCH 15/25] libcamera: pipeline: simple: enable use of Soft ISP and
|
||||
Date: Mon, 11 Mar 2024 15:15:16 +0100
|
||||
Subject: [PATCH 12/21] libcamera: pipeline: simple: enable use of Soft ISP and
|
||||
Soft IPA
|
||||
|
||||
To enable the Simple Soft ISP and Soft IPA for simple pipeline handler
|
||||
configure the build with:
|
||||
-Dpipelines=simple/simple -Dipas=simple/simple
|
||||
-Dpipelines=simple -Dipas=simple
|
||||
|
||||
Also using the Soft ISP for the particular hardware platform must
|
||||
be enabled in the supportedDevices[] table.
|
||||
|
||||
If the pipeline uses Converter, Soft ISP and Soft IPA aren't
|
||||
available.
|
||||
|
||||
Configuring the build with just:
|
||||
-Dpipelines=simple
|
||||
makes it to work like before - Soft ISP and Soft IPA aren't used.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/pipeline/simple/simple.cpp | 111 ++++++++++++++++++-----
|
||||
1 file changed, 89 insertions(+), 22 deletions(-)
|
||||
src/libcamera/pipeline/simple/simple.cpp | 137 ++++++++++++++++++-----
|
||||
1 file changed, 109 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
|
||||
index fa298746..c76510c2 100644
|
||||
index 78854ef8..c3ebb7b7 100644
|
||||
--- a/src/libcamera/pipeline/simple/simple.cpp
|
||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "libcamera/internal/device_enumerator.h"
|
||||
#include "libcamera/internal/media_device.h"
|
||||
#include "libcamera/internal/pipeline_handler.h"
|
||||
+#include "libcamera/internal/software_isp.h"
|
||||
+#include "libcamera/internal/software_isp/software_isp.h"
|
||||
#include "libcamera/internal/v4l2_subdevice.h"
|
||||
#include "libcamera/internal/v4l2_videodevice.h"
|
||||
|
||||
@@ -274,6 +275,7 @@ public:
|
||||
@@ -185,17 +186,22 @@ struct SimplePipelineInfo {
|
||||
* and the number of streams it supports.
|
||||
*/
|
||||
std::vector<std::pair<const char *, unsigned int>> converters;
|
||||
+ /*
|
||||
+ * Using Software ISP is to be enabled per driver.
|
||||
+ * The Software ISP can't be used together with the converters.
|
||||
+ */
|
||||
+ bool swIspEnabled;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
static const SimplePipelineInfo supportedDevices[] = {
|
||||
- { "dcmipp", {} },
|
||||
- { "imx7-csi", { { "pxp", 1 } } },
|
||||
- { "j721e-csi2rx", {} },
|
||||
- { "mxc-isi", {} },
|
||||
- { "qcom-camss", {} },
|
||||
- { "sun6i-csi", {} },
|
||||
+ { "dcmipp", {}, false },
|
||||
+ { "imx7-csi", { { "pxp", 1 } }, false },
|
||||
+ { "j721e-csi2rx", {}, false },
|
||||
+ { "mxc-isi", {}, false },
|
||||
+ { "qcom-camss", {}, true },
|
||||
+ { "sun6i-csi", {}, false },
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
@@ -274,6 +280,7 @@ public:
|
||||
bool useConversion_;
|
||||
|
||||
std::unique_ptr<Converter> converter_;
|
||||
|
@ -43,7 +72,7 @@ index fa298746..c76510c2 100644
|
|||
|
||||
private:
|
||||
void tryPipeline(unsigned int code, const Size &size);
|
||||
@@ -281,6 +283,9 @@ private:
|
||||
@@ -281,6 +288,9 @@ private:
|
||||
|
||||
void conversionInputDone(FrameBuffer *buffer);
|
||||
void conversionOutputDone(FrameBuffer *buffer);
|
||||
|
@ -53,58 +82,79 @@ index fa298746..c76510c2 100644
|
|||
};
|
||||
|
||||
class SimpleCameraConfiguration : public CameraConfiguration
|
||||
@@ -509,6 +514,25 @@ int SimpleCameraData::init()
|
||||
@@ -332,6 +342,7 @@ public:
|
||||
V4L2VideoDevice *video(const MediaEntity *entity);
|
||||
V4L2Subdevice *subdev(const MediaEntity *entity);
|
||||
MediaDevice *converter() { return converter_; }
|
||||
+ bool swIspEnabled() { return swIspEnabled_; }
|
||||
|
||||
protected:
|
||||
int queueRequestDevice(Camera *camera, Request *request) override;
|
||||
@@ -360,6 +371,7 @@ private:
|
||||
std::map<const MediaEntity *, EntityData> entities_;
|
||||
|
||||
MediaDevice *converter_;
|
||||
+ bool swIspEnabled_;
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
@@ -509,6 +521,29 @@ int SimpleCameraData::init()
|
||||
}
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Create SoftwareIsp unconditionally if no converter is used
|
||||
+ * - to be revisited
|
||||
+ * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
|
||||
+ */
|
||||
+ if (!converter_) {
|
||||
+ swIsp_ = SoftwareIspFactoryBase::create(pipe, sensor_->controls());
|
||||
+ if (!swIsp_) {
|
||||
+ if (!converter_ && pipe->swIspEnabled()) {
|
||||
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
|
||||
+ if (!swIsp_->isValid()) {
|
||||
+ LOG(SimplePipeline, Warning)
|
||||
+ << "Failed to create software ISP, disabling software debayering";
|
||||
+ swIsp_.reset();
|
||||
+ } else {
|
||||
+ swIsp_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
|
||||
+ /*
|
||||
+ * \todo explain why SimpleCameraData::conversionInputDone() can't be directly
|
||||
+ * connected to inputBufferReady signal.
|
||||
+ */
|
||||
+ swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) {
|
||||
+ this->conversionInputDone(buffer);
|
||||
+ });
|
||||
+ swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
|
||||
+ swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
|
||||
+
|
||||
+ swIsp_->getSignalSetSensorControls().connect(this, &SimpleCameraData::setSensorControls);
|
||||
+ swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
video_ = pipe->video(entities_.back().entity);
|
||||
ASSERT(video_);
|
||||
|
||||
@@ -599,12 +623,20 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
|
||||
@@ -599,12 +634,21 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
|
||||
config.captureFormat = pixelFormat;
|
||||
config.captureSize = format.size;
|
||||
|
||||
- if (!converter_) {
|
||||
- config.outputFormats = { pixelFormat };
|
||||
- config.outputSizes = config.captureSize;
|
||||
- } else {
|
||||
+
|
||||
+ if (converter_) {
|
||||
config.outputFormats = converter_->formats(pixelFormat);
|
||||
config.outputSizes = converter_->sizes(format.size);
|
||||
+ config.outputFormats = converter_->formats(pixelFormat);
|
||||
+ config.outputSizes = converter_->sizes(format.size);
|
||||
+ } else if (swIsp_) {
|
||||
+ config.outputFormats = swIsp_->formats(pixelFormat);
|
||||
+ config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
|
||||
+ if (config.outputFormats.empty()) {
|
||||
+ /* Do not use swIsp for unsupported pixelFormat's */
|
||||
+ /* Do not use swIsp for unsupported pixelFormat's: */
|
||||
+ config.outputFormats = { pixelFormat };
|
||||
+ config.outputSizes = config.captureSize;
|
||||
+ }
|
||||
+ } else {
|
||||
+ config.outputFormats = { pixelFormat };
|
||||
+ config.outputSizes = config.captureSize;
|
||||
config.outputFormats = { pixelFormat };
|
||||
config.outputSizes = config.captureSize;
|
||||
- } else {
|
||||
- config.outputFormats = converter_->formats(pixelFormat);
|
||||
- config.outputSizes = converter_->sizes(format.size);
|
||||
}
|
||||
|
||||
configs_.push_back(config);
|
||||
@@ -750,9 +782,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
@@ -750,9 +794,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -117,7 +167,7 @@ index fa298746..c76510c2 100644
|
|||
*/
|
||||
if (buffer->metadata().status != FrameMetadata::FrameCancelled)
|
||||
video_->queueBuffer(buffer);
|
||||
@@ -798,9 +830,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
@@ -798,9 +842,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
buffer->metadata().timestamp);
|
||||
|
||||
/*
|
||||
|
@ -130,7 +180,7 @@ index fa298746..c76510c2 100644
|
|||
*/
|
||||
if (useConversion_) {
|
||||
if (conversionQueue_.empty()) {
|
||||
@@ -808,7 +840,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
@@ -808,7 +852,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -143,7 +193,7 @@ index fa298746..c76510c2 100644
|
|||
conversionQueue_.pop();
|
||||
return;
|
||||
}
|
||||
@@ -834,6 +870,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
|
||||
@@ -834,6 +882,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
|
||||
pipe->completeRequest(request);
|
||||
}
|
||||
|
||||
|
@ -162,7 +212,7 @@ index fa298746..c76510c2 100644
|
|||
/* Retrieve all source pads connected to a sink pad through active routes. */
|
||||
std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
|
||||
{
|
||||
@@ -1016,8 +1064,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
|
||||
@@ -1046,8 +1106,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
|
||||
/* Set the stride, frameSize and bufferCount. */
|
||||
if (needConversion_) {
|
||||
std::tie(cfg.stride, cfg.frameSize) =
|
||||
|
@ -175,7 +225,7 @@ index fa298746..c76510c2 100644
|
|||
if (cfg.stride == 0)
|
||||
return Invalid;
|
||||
} else {
|
||||
@@ -1180,7 +1230,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
|
||||
@@ -1210,7 +1272,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
|
||||
inputCfg.stride = captureFormat.planes[0].bpl;
|
||||
inputCfg.bufferCount = kNumInternalBuffers;
|
||||
|
||||
|
@ -186,7 +236,7 @@ index fa298746..c76510c2 100644
|
|||
}
|
||||
|
||||
int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
|
||||
@@ -1194,8 +1246,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
|
||||
@@ -1224,8 +1288,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
|
||||
* whether the converter is used or not.
|
||||
*/
|
||||
if (data->useConversion_)
|
||||
|
@ -199,7 +249,7 @@ index fa298746..c76510c2 100644
|
|||
else
|
||||
return data->video_->exportBuffers(count, buffers);
|
||||
}
|
||||
@@ -1240,10 +1294,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
@@ -1270,10 +1336,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
|
||||
}
|
||||
|
||||
if (data->useConversion_) {
|
||||
|
@ -222,7 +272,7 @@ index fa298746..c76510c2 100644
|
|||
}
|
||||
|
||||
/* Queue all internal buffers for capture. */
|
||||
@@ -1259,8 +1321,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
@@ -1289,8 +1363,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
|
||||
SimpleCameraData *data = cameraData(camera);
|
||||
V4L2VideoDevice *video = data->video_;
|
||||
|
||||
|
@ -238,6 +288,15 @@ index fa298746..c76510c2 100644
|
|||
|
||||
video->streamOff();
|
||||
video->releaseBuffers();
|
||||
@@ -1452,6 +1531,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
|
||||
}
|
||||
}
|
||||
|
||||
+ swIspEnabled_ = info->swIspEnabled;
|
||||
+
|
||||
/* Locate the sensors. */
|
||||
std::vector<MediaEntity *> sensors = locateSensors();
|
||||
if (sensors.empty()) {
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,483 +0,0 @@
|
|||
From 21f1dd954a44b4e8f81abbfea276e4b60f8a8297 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Date: Thu, 23 Nov 2023 16:47:15 +0300
|
||||
Subject: [PATCH 13/25] libcamera: software_isp: add Simple SoftwareIsp
|
||||
implementation
|
||||
|
||||
The implementation of SoftwareIsp handles creation of Soft IPA
|
||||
and interactions with it, so that the pipeline handler wouldn't
|
||||
need to care about the Soft IPA.
|
||||
|
||||
Doxygen documentation by Dennis Bonke.
|
||||
|
||||
Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
---
|
||||
.../internal/software_isp/meson.build | 1 +
|
||||
.../internal/software_isp/swisp_simple.h | 163 ++++++++++++
|
||||
src/libcamera/software_isp/meson.build | 19 ++
|
||||
src/libcamera/software_isp/swisp_simple.cpp | 238 ++++++++++++++++++
|
||||
4 files changed, 421 insertions(+)
|
||||
create mode 100644 include/libcamera/internal/software_isp/swisp_simple.h
|
||||
create mode 100644 src/libcamera/software_isp/swisp_simple.cpp
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
|
||||
index b5a0d737..cf21ace5 100644
|
||||
--- a/include/libcamera/internal/software_isp/meson.build
|
||||
+++ b/include/libcamera/internal/software_isp/meson.build
|
||||
@@ -4,6 +4,7 @@ libcamera_internal_headers += files([
|
||||
'debayer.h',
|
||||
'debayer_cpu.h',
|
||||
'debayer_params.h',
|
||||
+ 'swisp_simple.h',
|
||||
'swisp_stats.h',
|
||||
'swstats.h',
|
||||
'swstats_cpu.h',
|
||||
diff --git a/include/libcamera/internal/software_isp/swisp_simple.h b/include/libcamera/internal/software_isp/swisp_simple.h
|
||||
new file mode 100644
|
||||
index 00000000..87613c23
|
||||
--- /dev/null
|
||||
+++ b/include/libcamera/internal/software_isp/swisp_simple.h
|
||||
@@ -0,0 +1,163 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * swisp_simple.h - Simple software ISP implementation
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+#include <libcamera/base/thread.h>
|
||||
+
|
||||
+#include <libcamera/pixel_format.h>
|
||||
+
|
||||
+#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
+#include <libcamera/ipa/soft_ipa_proxy.h>
|
||||
+
|
||||
+#include "libcamera/internal/dma_heaps.h"
|
||||
+#include "libcamera/internal/software_isp.h"
|
||||
+#include "libcamera/internal/software_isp/debayer_cpu.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+/**
|
||||
+ * \brief Class for the Simple Software ISP.
|
||||
+ *
|
||||
+ * Implementation of the SoftwareIsp interface.
|
||||
+ */
|
||||
+class SwIspSimple : public SoftwareIsp
|
||||
+{
|
||||
+public:
|
||||
+ /**
|
||||
+ * \brief Constructor for the SwIspSimple object.
|
||||
+ *
|
||||
+ * \param[in] pipe The pipeline handler in use.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ */
|
||||
+ SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
|
||||
+ ~SwIspSimple() {}
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Load a configuration from a file.
|
||||
+ * \param[in] filename The file to load from.
|
||||
+ *
|
||||
+ * \return 0 on success.
|
||||
+ */
|
||||
+ int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Gets if there is a valid debayer object.
|
||||
+ *
|
||||
+ * \returns true if there is, false otherwise.
|
||||
+ */
|
||||
+ bool isValid() const;
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the supported output formats.
|
||||
+ * \param[in] input The input format.
|
||||
+ *
|
||||
+ * \return all supported output formats or an empty vector if there are none.
|
||||
+ */
|
||||
+ std::vector<PixelFormat> formats(PixelFormat input);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the supported output sizes for the given input format and size.
|
||||
+ * \param[in] inputFormat The input format.
|
||||
+ * \param[in] inputSize The input size.
|
||||
+ *
|
||||
+ * \return The valid size ranges or an empty range if there are none.
|
||||
+ */
|
||||
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the stride and the frame size.
|
||||
+ * \param[in] outputFormat The output format.
|
||||
+ * \param[in] size The output size.
|
||||
+ *
|
||||
+ * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
|
||||
+ */
|
||||
+ std::tuple<unsigned int, unsigned int>
|
||||
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Configure the SwIspSimple object according to the passed in parameters.
|
||||
+ * \param[in] inputCfg The input configuration.
|
||||
+ * \param[in] outputCfgs The output configurations.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno on failure.
|
||||
+ */
|
||||
+ int configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
+ const ControlInfoMap &sensorControls);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Exports the buffers for use in processing.
|
||||
+ * \param[in] output The number of outputs requested.
|
||||
+ * \param[in] count The number of planes.
|
||||
+ * \param[out] buffers The exported buffers.
|
||||
+ *
|
||||
+ * \return count when successful, a negative return value if an error occurred.
|
||||
+ */
|
||||
+ int exportBuffers(unsigned int output, unsigned int count,
|
||||
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process the statistics gathered.
|
||||
+ * \param[in] sensorControls The sensor controls.
|
||||
+ */
|
||||
+ void processStats(const ControlList &sensorControls);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Starts the Software ISP worker.
|
||||
+ *
|
||||
+ * \return 0 on success, any other value indicates an error.
|
||||
+ */
|
||||
+ int start();
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Stops the Software ISP worker.
|
||||
+ */
|
||||
+ void stop();
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Queues buffers for processing.
|
||||
+ * \param[in] input The input framebuffer.
|
||||
+ * \param[in] outputs The output framebuffers.
|
||||
+ *
|
||||
+ * \return 0 on success, a negative errno on failure
|
||||
+ */
|
||||
+ int queueBuffers(FrameBuffer *input,
|
||||
+ const std::map<unsigned int, FrameBuffer *> &outputs);
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Get the signal for when the sensor controls are set.
|
||||
+ *
|
||||
+ * \return The control list of the sensor controls.
|
||||
+ */
|
||||
+ Signal<const ControlList &> &getSignalSetSensorControls();
|
||||
+
|
||||
+ /**
|
||||
+ * \brief Process the input framebuffer.
|
||||
+ * \param[in] input The input framebuffer.
|
||||
+ * \param[out] output The output framebuffer.
|
||||
+ */
|
||||
+ void process(FrameBuffer *input, FrameBuffer *output);
|
||||
+
|
||||
+private:
|
||||
+ void saveIspParams(int dummy);
|
||||
+ void statsReady(int dummy);
|
||||
+ void inputReady(FrameBuffer *input);
|
||||
+ void outputReady(FrameBuffer *output);
|
||||
+
|
||||
+ std::unique_ptr<DebayerCpu> debayer_;
|
||||
+ Thread ispWorkerThread_;
|
||||
+ SharedMemObject<DebayerParams> sharedParams_;
|
||||
+ DebayerParams debayerParams_;
|
||||
+ DmaHeap dmaHeap_;
|
||||
+
|
||||
+ std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
|
||||
+};
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
|
||||
index 6d7a44d7..9464f2fd 100644
|
||||
--- a/src/libcamera/software_isp/meson.build
|
||||
+++ b/src/libcamera/software_isp/meson.build
|
||||
@@ -6,3 +6,22 @@ libcamera_sources += files([
|
||||
'swstats.cpp',
|
||||
'swstats_cpu.cpp',
|
||||
])
|
||||
+
|
||||
+# Software ISP is enabled for 'simple' pipeline handler.
|
||||
+# The 'pipelines' option value should be
|
||||
+# 'simple/<Software ISP implementation>' e.g.:
|
||||
+# -Dpipelines=simple/simple
|
||||
+# The source file should be named swisp_<Software ISP implementation>.cpp,
|
||||
+# e.g. 'swisp_simple.cpp'.
|
||||
+
|
||||
+foreach pipeline : pipelines
|
||||
+ pipeline = pipeline.split('/')
|
||||
+ if pipeline.length() == 2 and pipeline[0] == 'simple'
|
||||
+ libcamera_sources += files([
|
||||
+ 'swisp_' + pipeline[1] + '.cpp',
|
||||
+ ])
|
||||
+ # the 'break' below can be removed if/when multiple
|
||||
+ # Software ISP implementations are allowed in single build
|
||||
+ break
|
||||
+ endif
|
||||
+endforeach
|
||||
diff --git a/src/libcamera/software_isp/swisp_simple.cpp b/src/libcamera/software_isp/swisp_simple.cpp
|
||||
new file mode 100644
|
||||
index 00000000..0884166e
|
||||
--- /dev/null
|
||||
+++ b/src/libcamera/software_isp/swisp_simple.cpp
|
||||
@@ -0,0 +1,238 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2023, Linaro Ltd
|
||||
+ *
|
||||
+ * swisp_simple.cpp - Simple software ISP implementation
|
||||
+ */
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/swisp_simple.h"
|
||||
+
|
||||
+#include <sys/mman.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include <libcamera/formats.h>
|
||||
+#include <libcamera/stream.h>
|
||||
+
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
+#include "libcamera/internal/framebuffer.h"
|
||||
+#include "libcamera/internal/ipa_manager.h"
|
||||
+#include "libcamera/internal/mapped_framebuffer.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+SwIspSimple::SwIspSimple(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
|
||||
+ : SoftwareIsp(pipe, sensorControls), debayer_(nullptr), debayerParams_{ 256, 256, 256, 0.5f },
|
||||
+ dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
||||
+{
|
||||
+ if (!dmaHeap_.isValid()) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
|
||||
+ if (!sharedParams_.fd().isValid()) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ std::unique_ptr<SwStatsCpu> stats;
|
||||
+
|
||||
+ stats = std::make_unique<SwStatsCpu>();
|
||||
+ if (!stats) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ stats->statsReady.connect(this, &SwIspSimple::statsReady);
|
||||
+
|
||||
+ debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
|
||||
+ if (!debayer_) {
|
||||
+ LOG(SoftwareIsp, Error) << "Failed to create DebayerCpu object";
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ debayer_->inputBufferReady.connect(this, &SwIspSimple::inputReady);
|
||||
+ debayer_->outputBufferReady.connect(this, &SwIspSimple::outputReady);
|
||||
+
|
||||
+ ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
|
||||
+ if (!ipa_) {
|
||||
+ LOG(SoftwareIsp, Error)
|
||||
+ << "Creating IPA for software ISP failed";
|
||||
+ debayer_.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
|
||||
+ debayer_->getStatsFD(),
|
||||
+ sharedParams_.fd(),
|
||||
+ sensorControls);
|
||||
+ if (ret) {
|
||||
+ LOG(SoftwareIsp, Error) << "IPA init failed";
|
||||
+ debayer_.reset();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ ipa_->setIspParams.connect(this, &SwIspSimple::saveIspParams);
|
||||
+
|
||||
+ debayer_->moveToThread(&ispWorkerThread_);
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::processStats(const ControlList &sensorControls)
|
||||
+{
|
||||
+ ASSERT(ipa_);
|
||||
+ ipa_->processStats(sensorControls);
|
||||
+}
|
||||
+
|
||||
+Signal<const ControlList &> &SwIspSimple::getSignalSetSensorControls()
|
||||
+{
|
||||
+ ASSERT(ipa_);
|
||||
+ return ipa_->setSensorControls;
|
||||
+}
|
||||
+
|
||||
+bool SwIspSimple::isValid() const
|
||||
+{
|
||||
+ return !!debayer_;
|
||||
+}
|
||||
+
|
||||
+std::vector<PixelFormat> SwIspSimple::formats(PixelFormat inputFormat)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->formats(inputFormat);
|
||||
+}
|
||||
+
|
||||
+SizeRange SwIspSimple::sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->sizes(inputFormat, inputSize);
|
||||
+}
|
||||
+
|
||||
+std::tuple<unsigned int, unsigned int>
|
||||
+SwIspSimple::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ return debayer_->strideAndFrameSize(outputFormat, size);
|
||||
+}
|
||||
+
|
||||
+int SwIspSimple::configure(const StreamConfiguration &inputCfg,
|
||||
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
|
||||
+ const ControlInfoMap &sensorControls)
|
||||
+{
|
||||
+ ASSERT(ipa_ != nullptr && debayer_ != nullptr);
|
||||
+
|
||||
+ int ret = ipa_->configure(sensorControls);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return debayer_->configure(inputCfg, outputCfgs);
|
||||
+}
|
||||
+
|
||||
+int SwIspSimple::exportBuffers(unsigned int output, unsigned int count,
|
||||
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
|
||||
+{
|
||||
+ ASSERT(debayer_ != nullptr);
|
||||
+
|
||||
+ /* single output for now */
|
||||
+ if (output >= 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (unsigned int i = 0; i < count; i++) {
|
||||
+ const std::string name = "frame-" + std::to_string(i);
|
||||
+ const size_t frameSize = debayer_->frameSize();
|
||||
+
|
||||
+ FrameBuffer::Plane outPlane;
|
||||
+ outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
|
||||
+ if (!outPlane.fd.isValid()) {
|
||||
+ LOG(SoftwareIsp, Error)
|
||||
+ << "failed to allocate a dma_buf";
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ outPlane.offset = 0;
|
||||
+ outPlane.length = frameSize;
|
||||
+
|
||||
+ std::vector<FrameBuffer::Plane> planes{ outPlane };
|
||||
+ buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
|
||||
+ }
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+int SwIspSimple::queueBuffers(FrameBuffer *input,
|
||||
+ const std::map<unsigned int, FrameBuffer *> &outputs)
|
||||
+{
|
||||
+ unsigned int mask = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Validate the outputs as a sanity check: at least one output is
|
||||
+ * required, all outputs must reference a valid stream and no two
|
||||
+ * outputs can reference the same stream.
|
||||
+ */
|
||||
+ if (outputs.empty())
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (auto [index, buffer] : outputs) {
|
||||
+ if (!buffer)
|
||||
+ return -EINVAL;
|
||||
+ if (index >= 1) /* only single stream atm */
|
||||
+ return -EINVAL;
|
||||
+ if (mask & (1 << index))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mask |= 1 << index;
|
||||
+ }
|
||||
+
|
||||
+ process(input, outputs.at(0));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int SwIspSimple::start()
|
||||
+{
|
||||
+ int ret = ipa_->start();
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ispWorkerThread_.start();
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::stop()
|
||||
+{
|
||||
+ ispWorkerThread_.exit();
|
||||
+ ispWorkerThread_.wait();
|
||||
+
|
||||
+ ipa_->stop();
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::process(FrameBuffer *input, FrameBuffer *output)
|
||||
+{
|
||||
+ debayer_->invokeMethod(&DebayerCpu::process,
|
||||
+ ConnectionTypeQueued, input, output, debayerParams_);
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::saveIspParams([[maybe_unused]] int dummy)
|
||||
+{
|
||||
+ debayerParams_ = *sharedParams_;
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::statsReady(int dummy)
|
||||
+{
|
||||
+ ispStatsReady.emit(dummy);
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::inputReady(FrameBuffer *input)
|
||||
+{
|
||||
+ inputBufferReady.emit(input);
|
||||
+}
|
||||
+
|
||||
+void SwIspSimple::outputReady(FrameBuffer *output)
|
||||
+{
|
||||
+ outputBufferReady.emit(output);
|
||||
+}
|
||||
+
|
||||
+REGISTER_SOFTWAREISP(SwIspSimple)
|
||||
+
|
||||
+} /* namespace libcamera */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From 66ef9f32e67a96655d10ba38f7a830b3bbfe50f2 Mon Sep 17 00:00:00 2001
|
||||
From aabc53453d542495d9da25411f57308c01f2bc28 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Thu, 14 Dec 2023 19:09:44 +0100
|
||||
Subject: [PATCH 16/25] libcamera: swstats_cpu: Add support for 8, 10 and 12
|
||||
Date: Mon, 11 Mar 2024 15:15:17 +0100
|
||||
Subject: [PATCH 13/21] libcamera: swstats_cpu: Add support for 8, 10 and 12
|
||||
bpp unpacked bayer input
|
||||
|
||||
Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
|
||||
|
@ -9,46 +9,21 @@ bayer orders.
|
|||
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/swstats_cpu.h | 9 ++
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 126 ++++++++++++++++++
|
||||
2 files changed, 135 insertions(+)
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 128 +++++++++++++++++++++
|
||||
src/libcamera/software_isp/swstats_cpu.h | 9 ++
|
||||
2 files changed, 137 insertions(+)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
index 8bb86e98..e7abc6bb 100644
|
||||
--- a/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
#include "libcamera/internal/shared_mem_object.h"
|
||||
#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
#include "libcamera/internal/software_isp/swstats.h"
|
||||
@@ -31,6 +32,14 @@ public:
|
||||
const SharedFD &getStatsFD() { return sharedStats_.fd(); }
|
||||
int configure(const StreamConfiguration &inputCfg);
|
||||
private:
|
||||
+ int setupStandardBayerOrder(BayerFormat::Order order);
|
||||
+ /* Bayer 8 bpp unpacked */
|
||||
+ void statsBGGR8Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 10 bpp unpacked */
|
||||
+ void statsBGGR10Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 12 bpp unpacked */
|
||||
+ void statsBGGR12Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 10 bpp packed */
|
||||
void statsBGGR10PLine0(const uint8_t *src[]);
|
||||
void statsGBRG10PLine0(const uint8_t *src[]);
|
||||
void resetStats(void);
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
index 59453d07..87550371 100644
|
||||
index 448d0e4c..be310f56 100644
|
||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
@@ -59,6 +59,83 @@ static const unsigned int BLUE_Y_MUL = 29; /* 0.11 * 256 */
|
||||
stats_.sumG_ += sumG; \
|
||||
@@ -71,6 +71,83 @@ static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
|
||||
stats_.sumG_ += sumG; \
|
||||
stats_.sumB_ += sumB;
|
||||
|
||||
+void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
|
||||
|
@ -56,9 +31,9 @@ index 59453d07..87550371 100644
|
|||
+ const uint8_t *src0 = src[1] + window_.x;
|
||||
+ const uint8_t *src1 = src[2] + window_.x;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint8_t)
|
||||
+ SWSTATS_START_LINE_STATS(uint8_t)
|
||||
+
|
||||
+ if (swap_lines_)
|
||||
+ if (swapLines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ /* x += 4 sample every other 2x2 block */
|
||||
|
@ -70,10 +45,10 @@ index 59453d07..87550371 100644
|
|||
+
|
||||
+ g = (g + g2) / 2;
|
||||
+
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(1)
|
||||
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+ SWSTATS_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
|
||||
|
@ -81,9 +56,9 @@ index 59453d07..87550371 100644
|
|||
+ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
|
||||
+ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint16_t)
|
||||
+ SWSTATS_START_LINE_STATS(uint16_t)
|
||||
+
|
||||
+ if (swap_lines_)
|
||||
+ if (swapLines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ /* x += 4 sample every other 2x2 block */
|
||||
|
@ -96,10 +71,10 @@ index 59453d07..87550371 100644
|
|||
+ g = (g + g2) / 2;
|
||||
+
|
||||
+ /* divide Y by 4 for 10 -> 8 bpp value */
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
|
||||
+ SWSTATS_ACCUMULATE_LINE_STATS(4)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+ SWSTATS_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
|
||||
|
@ -107,9 +82,9 @@ index 59453d07..87550371 100644
|
|||
+ const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
|
||||
+ const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint16_t)
|
||||
+ SWSTATS_START_LINE_STATS(uint16_t)
|
||||
+
|
||||
+ if (swap_lines_)
|
||||
+ if (swapLines_)
|
||||
+ std::swap(src0, src1);
|
||||
+
|
||||
+ /* x += 4 sample every other 2x2 block */
|
||||
|
@ -122,41 +97,44 @@ index 59453d07..87550371 100644
|
|||
+ g = (g + g2) / 2;
|
||||
+
|
||||
+ /* divide Y by 16 for 12 -> 8 bpp value */
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(16)
|
||||
+ SWSTATS_ACCUMULATE_LINE_STATS(16)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+ SWSTATS_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
static inline __attribute__((always_inline)) void
|
||||
statsBayer10P(const int width, const uint8_t *src0, const uint8_t *src1, bool bggr, SwIspStats &stats_)
|
||||
void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
|
||||
{
|
||||
@@ -124,6 +201,39 @@ void SwStatsCpu::finishStats(void)
|
||||
const uint8_t *src0 = src[1] + window_.x * 5 / 4;
|
||||
@@ -147,6 +224,42 @@ void SwStatsCpu::finishFrame(void)
|
||||
statsReady.emit(0);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Check if order is a standard Bayer order and setup x_shift_ and swap_lines_
|
||||
+/**
|
||||
+ * \brief Setup SwStatsCpu object for standard Bayer orders
|
||||
+ * \param[in] order The Bayer order
|
||||
+ *
|
||||
+ * Check if order is a standard Bayer order and setup xShift_ and swapLines_
|
||||
+ * so that a single BGGR stats function can be used for all 4 standard orders.
|
||||
+ */
|
||||
+int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order)
|
||||
+{
|
||||
+ switch (order) {
|
||||
+ case BayerFormat::BGGR:
|
||||
+ x_shift_ = 0;
|
||||
+ swap_lines_ = false;
|
||||
+ xShift_ = 0;
|
||||
+ swapLines_ = false;
|
||||
+ break;
|
||||
+ case BayerFormat::GBRG:
|
||||
+ x_shift_ = 1; /* BGGR -> GBRG */
|
||||
+ swap_lines_ = false;
|
||||
+ xShift_ = 1; /* BGGR -> GBRG */
|
||||
+ swapLines_ = false;
|
||||
+ break;
|
||||
+ case BayerFormat::GRBG:
|
||||
+ x_shift_ = 0;
|
||||
+ swap_lines_ = true; /* BGGR -> GRBG */
|
||||
+ xShift_ = 0;
|
||||
+ swapLines_ = true; /* BGGR -> GRBG */
|
||||
+ break;
|
||||
+ case BayerFormat::RGGB:
|
||||
+ x_shift_ = 1; /* BGGR -> GBRG */
|
||||
+ swap_lines_ = true; /* GBRG -> RGGB */
|
||||
+ xShift_ = 1; /* BGGR -> GBRG */
|
||||
+ swapLines_ = true; /* GBRG -> RGGB */
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
|
@ -164,36 +142,62 @@ index 59453d07..87550371 100644
|
|||
+
|
||||
+ patternSize_.height = 2;
|
||||
+ patternSize_.width = 2;
|
||||
+ y_skip_mask_ = 0x02; /* Skip every 3th and 4th line */
|
||||
+ ySkipMask_ = 0x02; /* Skip every 3th and 4th line */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
{
|
||||
/**
|
||||
* \brief Configure the statistics object for the passed in input format.
|
||||
* \param[in] inputCfg The input format
|
||||
@@ -158,6 +271,21 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
BayerFormat bayerFormat =
|
||||
@@ -132,6 +242,22 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
|
||||
finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
|
||||
BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
|
||||
|
||||
+ if (bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
+ setupStandardBayerOrder(bayerFormat.order) == 0) {
|
||||
+ bpp_ = (bayerFormat.bitDepth + 7) & ~7;
|
||||
+ switch (bayerFormat.bitDepth) {
|
||||
+ case 8:
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR8Line0;
|
||||
+ stats0_ = &SwStatsCpu::statsBGGR8Line0;
|
||||
+ return 0;
|
||||
+ case 10:
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR10Line0;
|
||||
+ stats0_ = &SwStatsCpu::statsBGGR10Line0;
|
||||
+ return 0;
|
||||
+ case 12:
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsBGGR12Line0;
|
||||
+ stats0_ = &SwStatsCpu::statsBGGR12Line0;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (bayerFormat.bitDepth == 10 &&
|
||||
bayerFormat.packing == BayerFormat::Packing::CSI2) {
|
||||
bpp_ = 10;
|
||||
patternSize_.height = 2;
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
|
||||
index 0ac9ae71..bbbcf69b 100644
|
||||
--- a/src/libcamera/software_isp/swstats_cpu.h
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.h
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <libcamera/geometry.h>
|
||||
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
#include "libcamera/internal/shared_mem_object.h"
|
||||
#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
|
||||
@@ -120,6 +121,14 @@ private:
|
||||
*/
|
||||
using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
|
||||
|
||||
+ int setupStandardBayerOrder(BayerFormat::Order order);
|
||||
+ /* Bayer 8 bpp unpacked */
|
||||
+ void statsBGGR8Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 10 bpp unpacked */
|
||||
+ void statsBGGR10Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 12 bpp unpacked */
|
||||
+ void statsBGGR12Line0(const uint8_t *src[]);
|
||||
+ /* Bayer 10 bpp packed */
|
||||
void statsBGGR10PLine0(const uint8_t *src[]);
|
||||
void statsGBRG10PLine0(const uint8_t *src[]);
|
||||
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From b7b211eb56d98d5b170bd73a23b55aeb45bde8c5 Mon Sep 17 00:00:00 2001
|
||||
From 5f3647bd4f12dd62256a425c49fd18a0f5990930 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Thu, 14 Dec 2023 19:57:15 +0100
|
||||
Subject: [PATCH 17/25] libcamera: debayer_cpu: Add support for 8, 10 and 12
|
||||
Date: Mon, 11 Mar 2024 15:15:18 +0100
|
||||
Subject: [PATCH 14/21] libcamera: debayer_cpu: Add support for 8, 10 and 12
|
||||
bpp unpacked bayer input
|
||||
|
||||
Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
|
||||
|
@ -9,80 +9,32 @@ bayer orders.
|
|||
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/debayer_cpu.h | 13 ++
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 128 ++++++++++++++++++
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 128 +++++++++++++++++++++
|
||||
src/libcamera/software_isp/debayer_cpu.h | 13 +++
|
||||
2 files changed, 141 insertions(+)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
index 78573f44..1147b368 100644
|
||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <libcamera/base/object.h>
|
||||
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
#include "libcamera/internal/software_isp/swstats_cpu.h"
|
||||
#include "libcamera/internal/software_isp/debayer.h"
|
||||
|
||||
@@ -75,11 +76,21 @@ public:
|
||||
* \return The output frame size.
|
||||
*/
|
||||
unsigned int frameSize() { return outputConfig_.frameSize; }
|
||||
+
|
||||
private:
|
||||
void initLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
void process2(const uint8_t *src, uint8_t *dst);
|
||||
void process4(const uint8_t *src, uint8_t *dst);
|
||||
+ /* 8-bit raw bayer format */
|
||||
+ void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ /* unpacked 10-bit raw bayer format */
|
||||
+ void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ /* unpacked 12-bit raw bayer format */
|
||||
+ void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
|
||||
void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
@@ -103,6 +114,7 @@ private:
|
||||
|
||||
int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
|
||||
int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
|
||||
+ int setupStandardBayerOrder(BayerFormat::Order order);
|
||||
int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
|
||||
|
||||
uint8_t gamma_[1024];
|
||||
@@ -119,6 +131,7 @@ private:
|
||||
std::unique_ptr<SwStatsCpu> stats_;
|
||||
uint8_t *lineBuffers_[5];
|
||||
unsigned int lineBufferIndex_;
|
||||
+ unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */
|
||||
bool enableInputMemcpy_;
|
||||
float gamma_correction_;
|
||||
int measuredFrames_;
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index e0c3c658..7b7623b7 100644
|
||||
index f932362c..eb1c2718 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -45,6 +45,11 @@ DebayerCpu::~DebayerCpu()
|
||||
@@ -56,6 +56,11 @@ DebayerCpu::~DebayerCpu()
|
||||
free(lineBuffers_[i]);
|
||||
}
|
||||
|
||||
+#define DECLARE_SRC_POINTERS(pixel_t) \
|
||||
+ const pixel_t *prev = (const pixel_t *)src[0] + x_shift_; \
|
||||
+ const pixel_t *curr = (const pixel_t *)src[1] + x_shift_; \
|
||||
+ const pixel_t *next = (const pixel_t *)src[2] + x_shift_;
|
||||
+#define DECLARE_SRC_POINTERS(pixel_t) \
|
||||
+ const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \
|
||||
+ const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \
|
||||
+ const pixel_t *next = (const pixel_t *)src[2] + xShift_;
|
||||
+
|
||||
// RGR
|
||||
// GBG
|
||||
// RGR
|
||||
@@ -81,6 +86,70 @@ DebayerCpu::~DebayerCpu()
|
||||
@@ -92,6 +97,70 @@ DebayerCpu::~DebayerCpu()
|
||||
*dst++ = red_[curr[x] / (div)]; \
|
||||
x++;
|
||||
|
||||
|
@ -153,7 +105,7 @@ index e0c3c658..7b7623b7 100644
|
|||
void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
|
||||
{
|
||||
const int width_in_bytes = window_.width * 5 / 4;
|
||||
@@ -170,6 +239,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
@@ -193,6 +262,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputFormat);
|
||||
|
||||
|
@ -170,12 +122,12 @@ index e0c3c658..7b7623b7 100644
|
|||
if (bayerFormat.bitDepth == 10 &&
|
||||
bayerFormat.packing == BayerFormat::Packing::CSI2 &&
|
||||
isStandardBayerOrder(bayerFormat.order)) {
|
||||
@@ -197,12 +276,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
|
||||
@@ -220,12 +299,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Check for standard Bayer orders and set x_shift_ and swap debayer0/1, so that
|
||||
+ * Check for standard Bayer orders and set xShift_ and swap debayer0/1, so that
|
||||
+ * a single pair of BGGR debayer functions can be used for all 4 standard orders.
|
||||
+ */
|
||||
+int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
|
||||
|
@ -184,13 +136,13 @@ index e0c3c658..7b7623b7 100644
|
|||
+ case BayerFormat::BGGR:
|
||||
+ break;
|
||||
+ case BayerFormat::GBRG:
|
||||
+ x_shift_ = 1; /* BGGR -> GBRG */
|
||||
+ xShift_ = 1; /* BGGR -> GBRG */
|
||||
+ break;
|
||||
+ case BayerFormat::GRBG:
|
||||
+ std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
|
||||
+ break;
|
||||
+ case BayerFormat::RGGB:
|
||||
+ x_shift_ = 1; /* BGGR -> GBRG */
|
||||
+ xShift_ = 1; /* BGGR -> GBRG */
|
||||
+ std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
|
||||
+ break;
|
||||
+ default:
|
||||
|
@ -206,7 +158,7 @@ index e0c3c658..7b7623b7 100644
|
|||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputFormat);
|
||||
|
||||
+ x_shift_ = 0;
|
||||
+ xShift_ = 0;
|
||||
+
|
||||
+ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
|
||||
+ bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
|
@ -232,6 +184,51 @@ index e0c3c658..7b7623b7 100644
|
|||
if (bayerFormat.bitDepth == 10 &&
|
||||
bayerFormat.packing == BayerFormat::Packing::CSI2) {
|
||||
switch (bayerFormat.order) {
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
|
||||
index 8a51ed85..fd1fa180 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.h
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.h
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <libcamera/base/object.h>
|
||||
|
||||
+#include "libcamera/internal/bayer_format.h"
|
||||
+
|
||||
#include "debayer.h"
|
||||
#include "swstats_cpu.h"
|
||||
|
||||
@@ -82,6 +84,15 @@ private:
|
||||
*/
|
||||
using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
|
||||
|
||||
+ /* 8-bit raw bayer format */
|
||||
+ void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ /* unpacked 10-bit raw bayer format */
|
||||
+ void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ /* unpacked 12-bit raw bayer format */
|
||||
+ void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
|
||||
void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
@@ -103,6 +114,7 @@ private:
|
||||
|
||||
int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
|
||||
int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
|
||||
+ int setupStandardBayerOrder(BayerFormat::Order order);
|
||||
int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
|
||||
void setupInputMemcpy(const uint8_t *linePointers[]);
|
||||
void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
|
||||
@@ -131,6 +143,7 @@ private:
|
||||
unsigned int lineBufferLength_;
|
||||
unsigned int lineBufferPadding_;
|
||||
unsigned int lineBufferIndex_;
|
||||
+ unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
|
||||
bool enableInputMemcpy_;
|
||||
float gamma_correction_;
|
||||
unsigned int measuredFrames_;
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,37 +1,27 @@
|
|||
From b835b2c90785ee02bc98888bf165713d16c24cc4 Mon Sep 17 00:00:00 2001
|
||||
From 186db51d54bcbd4d5096bea1e4396966c2dad001 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Mon, 18 Dec 2023 19:21:07 +0100
|
||||
Subject: [PATCH 18/25] libcamera: debayer_cpu: Add BGR888 output support
|
||||
Date: Mon, 11 Mar 2024 15:15:19 +0100
|
||||
Subject: [PATCH 15/21] libcamera: debayer_cpu: Add BGR888 output support
|
||||
|
||||
BGR888 is RGB888 with the red and blue pixels swapped, adjust
|
||||
the debayering to swap the red and blue pixels in the bayer pattern
|
||||
to add support for writing formats::BGR888.
|
||||
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
|
||||
Tested-by: Pavel Machek <pavel@ucw.cz>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
|
||||
---
|
||||
.../internal/software_isp/debayer_cpu.h | 1 +
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 43 ++++++++++++++++---
|
||||
2 files changed, 39 insertions(+), 5 deletions(-)
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 42 +++++++++++++++++++---
|
||||
src/libcamera/software_isp/debayer_cpu.h | 1 +
|
||||
2 files changed, 38 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
index 1147b368..bdeab7c0 100644
|
||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
@@ -133,6 +133,7 @@ private:
|
||||
unsigned int lineBufferIndex_;
|
||||
unsigned int x_shift_; /* Offset of 0/1 applied to window_.x */
|
||||
bool enableInputMemcpy_;
|
||||
+ bool swapRedBlueGains_;
|
||||
float gamma_correction_;
|
||||
int measuredFrames_;
|
||||
int64_t frameProcessTime_;
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index 7b7623b7..0edea4d3 100644
|
||||
index eb1c2718..a1692693 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -245,7 +245,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
@@ -268,7 +268,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
config.bpp = (bayerFormat.bitDepth + 7) & ~7;
|
||||
config.patternSize.width = 2;
|
||||
config.patternSize.height = 2;
|
||||
|
@ -40,7 +30,7 @@ index 7b7623b7..0edea4d3 100644
|
|||
return 0;
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
@@ -278,7 +278,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
config.bpp = 10;
|
||||
config.patternSize.width = 4; /* 5 bytes per *4* pixels */
|
||||
config.patternSize.height = 2;
|
||||
|
@ -49,7 +39,7 @@ index 7b7623b7..0edea4d3 100644
|
|||
return 0;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
@@ -289,7 +289,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
|
||||
int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
|
||||
{
|
||||
|
@ -58,7 +48,7 @@ index 7b7623b7..0edea4d3 100644
|
|||
config.bpp = 24;
|
||||
return 0;
|
||||
}
|
||||
@@ -302,12 +302,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
|
||||
@@ -325,13 +325,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -69,6 +59,7 @@ index 7b7623b7..0edea4d3 100644
|
|||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputFormat);
|
||||
|
||||
xShift_ = 0;
|
||||
+ swapRedBlueGains_ = false;
|
||||
+
|
||||
+ switch (outputFormat) {
|
||||
|
@ -98,11 +89,10 @@ index 7b7623b7..0edea4d3 100644
|
|||
+ default:
|
||||
+ goto invalid_fmt;
|
||||
+ }
|
||||
+
|
||||
x_shift_ = 0;
|
||||
|
||||
if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
|
||||
@@ -355,6 +384,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
|
||||
bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
@@ -378,6 +406,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,16 +100,28 @@ index 7b7623b7..0edea4d3 100644
|
|||
LOG(Debayer, Error) << "Unsupported input output format combination";
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -594,6 +624,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
|
||||
@@ -661,6 +690,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
|
||||
gamma_correction_ = params.gamma;
|
||||
}
|
||||
|
||||
+ if (swapRedBlueGains_)
|
||||
+ std::swap(params.gainR, params.gainB);
|
||||
+
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int idx;
|
||||
|
||||
for (unsigned int i = 0; i < kRGBLookupSize; i++) {
|
||||
constexpr unsigned int div =
|
||||
kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
|
||||
index fd1fa180..5f44fc65 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.h
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.h
|
||||
@@ -145,6 +145,7 @@ private:
|
||||
unsigned int lineBufferIndex_;
|
||||
unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
|
||||
bool enableInputMemcpy_;
|
||||
+ bool swapRedBlueGains_;
|
||||
float gamma_correction_;
|
||||
unsigned int measuredFrames_;
|
||||
int64_t frameProcessTime_;
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From e03beabbad83c4c283c7f1c2c4798b6c3e2eaf06 Mon Sep 17 00:00:00 2001
|
||||
From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Tue, 19 Dec 2023 11:16:26 +0100
|
||||
Subject: [PATCH 20/25] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
|
||||
Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
|
||||
order DNU
|
||||
|
||||
The ov01a1s sensor has the following bayer pattern (4x4 tile repeating):
|
||||
|
@ -233,5 +233,5 @@ index 15e8206a..4ad37aaf 100644
|
|||
{ MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
|
||||
{ MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
From 26e96232c314f9d34f6ee3be365c04918967084e Mon Sep 17 00:00:00 2001
|
||||
From 6c509a3d144d46a11454d32d128d16e16602b50f Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Mon, 22 Jan 2024 17:18:00 +0100
|
||||
Subject: [PATCH 23/25] libcamera: Add "Software ISP benchmarking"
|
||||
Date: Mon, 11 Mar 2024 15:15:20 +0100
|
||||
Subject: [PATCH 17/21] libcamera: Add "Software ISP benchmarking"
|
||||
documentation
|
||||
|
||||
Add a "Software ISP benchmarking" documentation section which describes
|
||||
the performance/power consumption measurements used during
|
||||
the Software ISP's development.
|
||||
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
|
||||
---
|
||||
Documentation/index.rst | 1 +
|
||||
Documentation/meson.build | 1 +
|
||||
|
@ -39,7 +41,7 @@ index 7a58fec8..3872e0a8 100644
|
|||
|
||||
diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst
|
||||
new file mode 100644
|
||||
index 00000000..738c8c65
|
||||
index 00000000..b2803953
|
||||
--- /dev/null
|
||||
+++ b/Documentation/software-isp-benchmarking.rst
|
||||
@@ -0,0 +1,82 @@
|
||||
|
@ -50,8 +52,8 @@ index 00000000..738c8c65
|
|||
+Software ISP benchmarking
|
||||
+=========================
|
||||
+
|
||||
+The Software ISP is paricular sensitive to performance regressions
|
||||
+therefor it is a good idea to always benchmark the Software ISP
|
||||
+The Software ISP is particularly sensitive to performance regressions
|
||||
+therefore it is a good idea to always benchmark the Software ISP
|
||||
+before and after making changes to it and ensure that there are
|
||||
+no performance regressions.
|
||||
+
|
||||
|
@ -59,8 +61,8 @@ index 00000000..738c8c65
|
|||
+----------------------------------
|
||||
+
|
||||
+The DebayerCpu class has a builtin benchmark. This benchmark
|
||||
+measures the time spend on processing (collecting statistics
|
||||
+and debayering) only, it does not measure the time spend on
|
||||
+measures the time spent on processing (collecting statistics
|
||||
+and debayering) only, it does not measure the time spent on
|
||||
+capturing or outputting the frames.
|
||||
+
|
||||
+The builtin benchmark always runs. So this can be used by simply
|
||||
|
@ -71,7 +73,7 @@ index 00000000..738c8c65
|
|||
+and then it measures 30 fps and shows the total and per frame
|
||||
+processing time using an info level log message:
|
||||
+
|
||||
+.. code-block::
|
||||
+.. code-block:: text
|
||||
+
|
||||
+ INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame
|
||||
+
|
||||
|
@ -83,7 +85,7 @@ index 00000000..738c8c65
|
|||
+For example when benchmarking on a Lenovo ThinkPad X1 Yoga Gen 8, with
|
||||
+the charger plugged in, the CPU can be fixed to run at 2 GHz using:
|
||||
+
|
||||
+.. code-block::
|
||||
+.. code-block:: shell
|
||||
+
|
||||
+ sudo x86_energy_perf_policy --turbo-enable 0
|
||||
+ sudo cpupower frequency-set -d 2GHz -u 2GHz
|
||||
|
@ -107,14 +109,14 @@ index 00000000..738c8c65
|
|||
+so that it is fully visible. After this run the following command to monitor
|
||||
+the power consumption:
|
||||
+
|
||||
+.. code-block::
|
||||
+.. code-block:: shell
|
||||
+
|
||||
+ watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input
|
||||
+
|
||||
+Note this not only measures the power consumption in ųW it also monitors
|
||||
+Note this not only measures the power consumption in µW it also monitors
|
||||
+the speed of this laptop's 2 fans. This is important because depending on
|
||||
+the ambient temperature the 2 fans may spin up while testing and this
|
||||
+will cause an additional power consumption of approx. 0.5W messing up
|
||||
+will cause an additional power consumption of approx. 0.5 W messing up
|
||||
+the measurement.
|
||||
+
|
||||
+After starting qcam + the watch command let the laptop sit without using
|
||||
|
@ -123,8 +125,8 @@ index 00000000..738c8c65
|
|||
+and avarage these.
|
||||
+
|
||||
+On the example Lenovo ThinkPad X1 Yoga Gen 8 laptop this results in
|
||||
+a measured power consumption of approx. 13W while running qcam versus
|
||||
+approx. 4-5W while setting idle with its OLED panel on.
|
||||
+a measured power consumption of approx. 13 W while running qcam versus
|
||||
+approx. 4-5 W while setting idle with its OLED panel on.
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001
|
||||
From: Milan Zamazal <mzamazal@redhat.com>
|
||||
Date: Mon, 11 Mar 2024 15:15:21 +0100
|
||||
Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation
|
||||
|
||||
Black may not be represented as 0 pixel value for given hardware, it may be
|
||||
higher. If this is not compensated then various problems may occur such as low
|
||||
contrast or suboptimal exposure.
|
||||
|
||||
The black pixel value can be either retrieved from a tuning file for the given
|
||||
hardware, or automatically on fly. The former is the right and correct method,
|
||||
while the latter can be used when a tuning file is not available for the given
|
||||
hardware. Since there is currently no support for tuning files in software ISP,
|
||||
the automatic, hardware independent way, is always used. Support for tuning
|
||||
files should be added in future but it will require more work than this patch.
|
||||
|
||||
The patch looks at the image histogram and assumes that black starts when pixel
|
||||
values start occurring on the left. A certain amount of the darkest pixels is
|
||||
ignored; it doesn't matter whether they represent various kinds of noise or are
|
||||
real, they are better to omit in any case to make the image looking better. It
|
||||
also doesn't matter whether the darkest pixels occur around the supposed black
|
||||
level or are spread between 0 and the black level, the difference is not
|
||||
important.
|
||||
|
||||
An arbitrary threshold of 2% darkest pixels is applied; there is no magic about
|
||||
that value.
|
||||
|
||||
The patch assumes that the black values for different colors are the same and
|
||||
doesn't attempt any other non-primitive enhancements. It cannot completely
|
||||
replace tuning files and simplicity, while providing visible benefit, is its
|
||||
goal. Anything more sophisticated is left for future patches.
|
||||
|
||||
A possible cheap enhancement, if needed, could be setting exposure + gain to
|
||||
minimum values temporarily, before setting the black level. In theory, the
|
||||
black level should be fixed but it may not be reached in all images. For this
|
||||
reason, the patch updates black level only if the observed value is lower than
|
||||
the current one; it should be never increased.
|
||||
|
||||
The purpose of the patch is to compensate for hardware properties. General
|
||||
image contrast enhancements are out of scope of this patch.
|
||||
|
||||
Stats are still gathered as an uncorrected histogram, to avoid any confusion and
|
||||
to represent the raw image data. Exposure must be determined after the black
|
||||
level correction -- it has no influence on the sub-black area and must be
|
||||
correct after applying the black level correction. The granularity of the
|
||||
histogram is increased from 16 to 64 to provide a better precision (there is no
|
||||
theory behind either of those numbers).
|
||||
|
||||
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/debayer_params.h | 4 +
|
||||
.../internal/software_isp/swisp_stats.h | 10 ++-
|
||||
src/ipa/simple/black_level.cpp | 86 +++++++++++++++++++
|
||||
src/ipa/simple/black_level.h | 28 ++++++
|
||||
src/ipa/simple/meson.build | 7 +-
|
||||
src/ipa/simple/soft_simple.cpp | 28 ++++--
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 13 ++-
|
||||
src/libcamera/software_isp/debayer_cpu.h | 1 +
|
||||
src/libcamera/software_isp/software_isp.cpp | 2 +-
|
||||
9 files changed, 162 insertions(+), 17 deletions(-)
|
||||
create mode 100644 src/ipa/simple/black_level.cpp
|
||||
create mode 100644 src/ipa/simple/black_level.h
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
index 98965fa1..5e38e08b 100644
|
||||
--- a/include/libcamera/internal/software_isp/debayer_params.h
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_params.h
|
||||
@@ -43,6 +43,10 @@ struct DebayerParams {
|
||||
* \brief Gamma correction, 1.0 is no correction
|
||||
*/
|
||||
float gamma;
|
||||
+ /**
|
||||
+ * \brief Level of the black point, 0..255, 0 is no correction.
|
||||
+ */
|
||||
+ unsigned int blackLevel;
|
||||
};
|
||||
|
||||
} /* namespace libcamera */
|
||||
diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
index afe42c9a..25cd5abd 100644
|
||||
--- a/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
+++ b/include/libcamera/internal/software_isp/swisp_stats.h
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
+#include <array>
|
||||
+
|
||||
namespace libcamera {
|
||||
|
||||
/**
|
||||
@@ -28,11 +30,15 @@ struct SwIspStats {
|
||||
/**
|
||||
* \brief Number of bins in the yHistogram.
|
||||
*/
|
||||
- static constexpr unsigned int kYHistogramSize = 16;
|
||||
+ static constexpr unsigned int kYHistogramSize = 64;
|
||||
+ /**
|
||||
+ * \brief Type of the histogram.
|
||||
+ */
|
||||
+ using histogram = std::array<unsigned int, kYHistogramSize>;
|
||||
/**
|
||||
* \brief A histogram of luminance values.
|
||||
*/
|
||||
- std::array<unsigned int, kYHistogramSize> yHistogram;
|
||||
+ histogram yHistogram;
|
||||
};
|
||||
|
||||
} /* namespace libcamera */
|
||||
diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp
|
||||
new file mode 100644
|
||||
index 00000000..8d52201b
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/black_level.cpp
|
||||
@@ -0,0 +1,86 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2024, Red Hat Inc.
|
||||
+ *
|
||||
+ * black_level.cpp - black level handling
|
||||
+ */
|
||||
+
|
||||
+#include "black_level.h"
|
||||
+
|
||||
+#include <numeric>
|
||||
+
|
||||
+#include <libcamera/base/log.h>
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+LOG_DEFINE_CATEGORY(IPASoftBL)
|
||||
+
|
||||
+/**
|
||||
+ * \class BlackLevel
|
||||
+ * \brief Object providing black point level for software ISP
|
||||
+ *
|
||||
+ * Black level can be provided in hardware tuning files or, if no tuning file is
|
||||
+ * available for the given hardware, guessed automatically, with less accuracy.
|
||||
+ * As tuning files are not yet implemented for software ISP, BlackLevel
|
||||
+ * currently provides only guessed black levels.
|
||||
+ *
|
||||
+ * This class serves for tracking black level as a property of the underlying
|
||||
+ * hardware, not as means of enhancing a particular scene or image.
|
||||
+ *
|
||||
+ * The class is supposed to be instantiated for the given camera stream.
|
||||
+ * The black level can be retrieved using BlackLevel::get() method. It is
|
||||
+ * initially 0 and may change when updated using BlackLevel::update() method.
|
||||
+ */
|
||||
+
|
||||
+BlackLevel::BlackLevel()
|
||||
+ : blackLevel_(255), blackLevelSet_(false)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Return the current black level
|
||||
+ *
|
||||
+ * \return The black level, in the range from 0 (minimum) to 255 (maximum).
|
||||
+ * If the black level couldn't be determined yet, return 0.
|
||||
+ */
|
||||
+unsigned int BlackLevel::get() const
|
||||
+{
|
||||
+ return blackLevelSet_ ? blackLevel_ : 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * \brief Update black level from the provided histogram
|
||||
+ * \param[in] yHistogram The histogram to be used for updating black level
|
||||
+ *
|
||||
+ * The black level is property of the given hardware, not image. It is updated
|
||||
+ * only if it has not been yet set or if it is lower than the lowest value seen
|
||||
+ * so far.
|
||||
+ */
|
||||
+void BlackLevel::update(SwIspStats::histogram &yHistogram)
|
||||
+{
|
||||
+ // The constant is selected to be "good enough", not overly conservative or
|
||||
+ // aggressive. There is no magic about the given value.
|
||||
+ constexpr float ignoredPercentage_ = 0.02;
|
||||
+ const unsigned int total =
|
||||
+ std::accumulate(begin(yHistogram), end(yHistogram), 0);
|
||||
+ const unsigned int pixelThreshold = ignoredPercentage_ * total;
|
||||
+ const unsigned int currentBlackIdx =
|
||||
+ blackLevel_ / (256 / SwIspStats::kYHistogramSize);
|
||||
+
|
||||
+ for (unsigned int i = 0, seen = 0;
|
||||
+ i < currentBlackIdx && i < SwIspStats::kYHistogramSize;
|
||||
+ i++) {
|
||||
+ seen += yHistogram[i];
|
||||
+ if (seen >= pixelThreshold) {
|
||||
+ blackLevel_ = i * (256 / SwIspStats::kYHistogramSize);
|
||||
+ blackLevelSet_ = true;
|
||||
+ LOG(IPASoftBL, Debug)
|
||||
+ << "Auto-set black level: "
|
||||
+ << i << "/" << SwIspStats::kYHistogramSize
|
||||
+ << " (" << 100 * (seen - yHistogram[i]) / total << "% below, "
|
||||
+ << 100 * seen / total << "% at or below)";
|
||||
+ break;
|
||||
+ }
|
||||
+ };
|
||||
+}
|
||||
+} // namespace libcamera
|
||||
diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h
|
||||
new file mode 100644
|
||||
index 00000000..b3785db0
|
||||
--- /dev/null
|
||||
+++ b/src/ipa/simple/black_level.h
|
||||
@@ -0,0 +1,28 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+/*
|
||||
+ * Copyright (C) 2024, Red Hat Inc.
|
||||
+ *
|
||||
+ * black_level.h - black level handling
|
||||
+ */
|
||||
+
|
||||
+#pragma once
|
||||
+
|
||||
+#include <array>
|
||||
+
|
||||
+#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
+
|
||||
+namespace libcamera {
|
||||
+
|
||||
+class BlackLevel
|
||||
+{
|
||||
+public:
|
||||
+ BlackLevel();
|
||||
+ unsigned int get() const;
|
||||
+ void update(std::array<unsigned int, SwIspStats::kYHistogramSize> &yHistogram);
|
||||
+
|
||||
+private:
|
||||
+ unsigned int blackLevel_;
|
||||
+ bool blackLevelSet_;
|
||||
+};
|
||||
+
|
||||
+} // namespace libcamera
|
||||
diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
|
||||
index 3e863db7..44b5f1d7 100644
|
||||
--- a/src/ipa/simple/meson.build
|
||||
+++ b/src/ipa/simple/meson.build
|
||||
@@ -2,8 +2,13 @@
|
||||
|
||||
ipa_name = 'ipa_soft_simple'
|
||||
|
||||
+soft_simple_sources = files([
|
||||
+ 'soft_simple.cpp',
|
||||
+ 'black_level.cpp',
|
||||
+])
|
||||
+
|
||||
mod = shared_module(ipa_name,
|
||||
- ['soft_simple.cpp', libcamera_generated_ipa_headers],
|
||||
+ [soft_simple_sources, libcamera_generated_ipa_headers],
|
||||
name_prefix : '',
|
||||
include_directories : [ipa_includes, libipa_includes],
|
||||
dependencies : libcamera_private,
|
||||
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
|
||||
index 312df4ba..ac027568 100644
|
||||
--- a/src/ipa/simple/soft_simple.cpp
|
||||
+++ b/src/ipa/simple/soft_simple.cpp
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
|
||||
+#include "black_level.h"
|
||||
+
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DEFINE_CATEGORY(IPASoft)
|
||||
@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface
|
||||
public:
|
||||
IPASoftSimple()
|
||||
: params_(static_cast<DebayerParams *>(MAP_FAILED)),
|
||||
- stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
|
||||
+ stats_(static_cast<SwIspStats *>(MAP_FAILED)),
|
||||
+ blackLevel_(BlackLevel()), ignore_updates_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -63,6 +66,7 @@ private:
|
||||
SharedFD fdParams_;
|
||||
DebayerParams *params_;
|
||||
SwIspStats *stats_;
|
||||
+ BlackLevel blackLevel_;
|
||||
|
||||
int32_t exposure_min_, exposure_max_;
|
||||
int32_t again_min_, again_max_;
|
||||
@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
|
||||
params_->gainG = 256;
|
||||
params_->gamma = 0.5;
|
||||
|
||||
+ if (ignore_updates_ > 0)
|
||||
+ blackLevel_.update(stats_->yHistogram);
|
||||
+ params_->blackLevel = blackLevel_.get();
|
||||
+
|
||||
setIspParams.emit(0);
|
||||
|
||||
/*
|
||||
@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
|
||||
* Calculate Mean Sample Value (MSV) according to formula from:
|
||||
* https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
|
||||
*/
|
||||
- constexpr unsigned int yHistValsPerBin =
|
||||
- SwIspStats::kYHistogramSize / kExposureBinsCount;
|
||||
- constexpr unsigned int yHistValsPerBinMod =
|
||||
- SwIspStats::kYHistogramSize /
|
||||
- (SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
|
||||
+ const unsigned int blackLevelHistIdx =
|
||||
+ params_->blackLevel / (256 / SwIspStats::kYHistogramSize);
|
||||
+ const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx;
|
||||
+ const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
|
||||
+ const unsigned int yHistValsPerBinMod =
|
||||
+ histogramSize / (histogramSize % kExposureBinsCount + 1);
|
||||
int ExposureBins[kExposureBinsCount] = {};
|
||||
unsigned int denom = 0;
|
||||
unsigned int num = 0;
|
||||
|
||||
- for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
|
||||
+ for (unsigned int i = 0; i < histogramSize; i++) {
|
||||
unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
|
||||
- ExposureBins[idx] += stats_->yHistogram[i];
|
||||
+ ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i];
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < kExposureBinsCount; i++) {
|
||||
@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
|
||||
|
||||
LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
|
||||
<< " exp " << exposure_ << " again " << again_
|
||||
- << " gain R/B " << params_->gainR << "/" << params_->gainB;
|
||||
+ << " gain R/B " << params_->gainR << "/" << params_->gainB
|
||||
+ << " black level " << params_->blackLevel;
|
||||
}
|
||||
|
||||
void IPASoftSimple::updateExposure(double exposureMSV)
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index a1692693..3be3cdfe 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -35,7 +35,7 @@ namespace libcamera {
|
||||
* \param[in] stats Pointer to the stats object to use.
|
||||
*/
|
||||
DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
|
||||
- : stats_(std::move(stats)), gamma_correction_(1.0)
|
||||
+ : stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
enableInputMemcpy_ = false;
|
||||
@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
|
||||
}
|
||||
|
||||
/* Apply DebayerParams */
|
||||
- if (params.gamma != gamma_correction_) {
|
||||
- for (unsigned int i = 0; i < kGammaLookupSize; i++)
|
||||
- gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
|
||||
+ if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) {
|
||||
+ const unsigned int blackIndex =
|
||||
+ params.blackLevel * kGammaLookupSize / 256;
|
||||
+ std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0);
|
||||
+ const float divisor = kGammaLookupSize - blackIndex - 1.0;
|
||||
+ for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
|
||||
+ gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma);
|
||||
|
||||
gamma_correction_ = params.gamma;
|
||||
+ blackLevel_ = params.blackLevel;
|
||||
}
|
||||
|
||||
if (swapRedBlueGains_)
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
|
||||
index 5f44fc65..ea02f909 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.h
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.h
|
||||
@@ -147,6 +147,7 @@ private:
|
||||
bool enableInputMemcpy_;
|
||||
bool swapRedBlueGains_;
|
||||
float gamma_correction_;
|
||||
+ unsigned int blackLevel_;
|
||||
unsigned int measuredFrames_;
|
||||
int64_t frameProcessTime_;
|
||||
/* Skip 30 frames for things to stabilize then measure 30 frames */
|
||||
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
|
||||
index 388b4496..9b49be41 100644
|
||||
--- a/src/libcamera/software_isp/software_isp.cpp
|
||||
+++ b/src/libcamera/software_isp/software_isp.cpp
|
||||
@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
|
||||
*/
|
||||
SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
|
||||
: debayer_(nullptr),
|
||||
- debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
|
||||
+ debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
|
||||
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
||||
{
|
||||
if (!dmaHeap_.isValid()) {
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
|
||||
Date: Mon, 11 Mar 2024 15:15:22 +0100
|
||||
Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for
|
||||
analogue gain
|
||||
|
||||
Use CameraSensorHelper to convert the analogue gain code read from the
|
||||
camera sensor into real analogue gain value. In the future this makes
|
||||
it possible to use faster AE/AGC algorithm. For now the same AE/AGC
|
||||
algorithm is used, but even then the CameraSensorHelper lets us use the
|
||||
full range of analogue gain values.
|
||||
|
||||
If there is no CameraSensorHelper for the camera sensor in use, a
|
||||
warning log message is printed, and the AE/AGC works exactly as before
|
||||
this change.
|
||||
|
||||
Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
|
||||
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/software_isp.h | 3 +-
|
||||
src/ipa/simple/soft_simple.cpp | 77 ++++++++++++-------
|
||||
src/libcamera/pipeline/simple/simple.cpp | 2 +-
|
||||
src/libcamera/software_isp/software_isp.cpp | 8 +-
|
||||
4 files changed, 57 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
|
||||
index 8d25e979..2a6db7ba 100644
|
||||
--- a/include/libcamera/internal/software_isp/software_isp.h
|
||||
+++ b/include/libcamera/internal/software_isp/software_isp.h
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <libcamera/ipa/soft_ipa_interface.h>
|
||||
#include <libcamera/ipa/soft_ipa_proxy.h>
|
||||
|
||||
+#include "libcamera/internal/camera_sensor.h"
|
||||
#include "libcamera/internal/dma_heaps.h"
|
||||
#include "libcamera/internal/pipeline_handler.h"
|
||||
#include "libcamera/internal/shared_mem_object.h"
|
||||
@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)
|
||||
class SoftwareIsp
|
||||
{
|
||||
public:
|
||||
- SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
|
||||
+ SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
|
||||
~SoftwareIsp();
|
||||
|
||||
int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
|
||||
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
|
||||
index ac027568..e4d64762 100644
|
||||
--- a/src/ipa/simple/soft_simple.cpp
|
||||
+++ b/src/ipa/simple/soft_simple.cpp
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "libcamera/internal/software_isp/debayer_params.h"
|
||||
#include "libcamera/internal/software_isp/swisp_stats.h"
|
||||
|
||||
+#include "libipa/camera_sensor_helper.h"
|
||||
+
|
||||
#include "black_level.h"
|
||||
|
||||
namespace libcamera {
|
||||
@@ -67,18 +69,27 @@ private:
|
||||
DebayerParams *params_;
|
||||
SwIspStats *stats_;
|
||||
BlackLevel blackLevel_;
|
||||
+ std::unique_ptr<CameraSensorHelper> camHelper_;
|
||||
|
||||
int32_t exposure_min_, exposure_max_;
|
||||
- int32_t again_min_, again_max_;
|
||||
- int32_t again_, exposure_;
|
||||
+ int32_t exposure_;
|
||||
+ double again_min_, again_max_, againMinStep_;
|
||||
+ double again_;
|
||||
unsigned int ignore_updates_;
|
||||
};
|
||||
|
||||
-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
|
||||
+int IPASoftSimple::init(const IPASettings &settings,
|
||||
const SharedFD &fdStats,
|
||||
const SharedFD &fdParams,
|
||||
const ControlInfoMap &sensorInfoMap)
|
||||
{
|
||||
+ camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
|
||||
+ if (camHelper_ == nullptr) {
|
||||
+ LOG(IPASoft, Warning)
|
||||
+ << "Failed to create camera sensor helper for "
|
||||
+ << settings.sensorModel;
|
||||
+ }
|
||||
+
|
||||
fdStats_ = fdStats;
|
||||
if (!fdStats_.isValid()) {
|
||||
LOG(IPASoft, Error) << "Invalid Statistics handle";
|
||||
@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
|
||||
exposure_min_ = 1;
|
||||
}
|
||||
|
||||
- again_min_ = gain_info.min().get<int32_t>();
|
||||
- again_max_ = gain_info.max().get<int32_t>();
|
||||
- /*
|
||||
- * The camera sensor gain (g) is usually not equal to the value written
|
||||
- * into the gain register (x). But the way how the AGC algorithm changes
|
||||
- * the gain value to make the total exposure closer to the optimum assumes
|
||||
- * that g(x) is not too far from linear function. If the minimal gain is 0,
|
||||
- * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
|
||||
- * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
|
||||
- * one edge, and very small near the other) we limit the range of the gain
|
||||
- * values used.
|
||||
- */
|
||||
- if (!again_min_) {
|
||||
- LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
|
||||
- again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
|
||||
+ int32_t again_min = gain_info.min().get<int32_t>();
|
||||
+ int32_t again_max = gain_info.max().get<int32_t>();
|
||||
+
|
||||
+ if (camHelper_) {
|
||||
+ again_min_ = camHelper_->gain(again_min);
|
||||
+ again_max_ = camHelper_->gain(again_max);
|
||||
+ againMinStep_ = (again_max_ - again_min_) / 100.0;
|
||||
+ } else {
|
||||
+ /*
|
||||
+ * The camera sensor gain (g) is usually not equal to the value written
|
||||
+ * into the gain register (x). But the way how the AGC algorithm changes
|
||||
+ * the gain value to make the total exposure closer to the optimum assumes
|
||||
+ * that g(x) is not too far from linear function. If the minimal gain is 0,
|
||||
+ * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
|
||||
+ * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
|
||||
+ * one edge, and very small near the other) we limit the range of the gain
|
||||
+ * values used.
|
||||
+ */
|
||||
+ again_max_ = again_max;
|
||||
+ if (!again_min) {
|
||||
+ LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
|
||||
+ again_min_ = std::min(100, again_min / 2 + again_max / 2);
|
||||
+ }
|
||||
+ againMinStep_ = 1.0;
|
||||
}
|
||||
|
||||
LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
|
||||
- << ", gain " << again_min_ << "-" << again_max_;
|
||||
+ << ", gain " << again_min_ << "-" << again_max_
|
||||
+ << " (" << againMinStep_ << ")";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
|
||||
ControlList ctrls(sensorControls);
|
||||
|
||||
exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
|
||||
- again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
|
||||
+ int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
|
||||
+ again_ = camHelper_ ? camHelper_->gain(again) : again;
|
||||
|
||||
updateExposure(exposureMSV);
|
||||
|
||||
ctrls.set(V4L2_CID_EXPOSURE, exposure_);
|
||||
- ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
|
||||
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN,
|
||||
+ static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));
|
||||
|
||||
ignore_updates_ = 2;
|
||||
|
||||
@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)
|
||||
static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
|
||||
static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
|
||||
|
||||
- int next;
|
||||
+ double next;
|
||||
|
||||
if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
|
||||
next = exposure_ * kExpNumeratorUp / kExpDenominator;
|
||||
@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)
|
||||
exposure_ = next;
|
||||
if (exposure_ >= exposure_max_) {
|
||||
next = again_ * kExpNumeratorUp / kExpDenominator;
|
||||
- if (next - again_ < 1)
|
||||
- again_ += 1;
|
||||
+ if (next - again_ < againMinStep_)
|
||||
+ again_ += againMinStep_;
|
||||
else
|
||||
again_ = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
|
||||
- if (exposure_ == exposure_max_ && again_ != again_min_) {
|
||||
+ if (exposure_ == exposure_max_ && again_ > again_min_) {
|
||||
next = again_ * kExpNumeratorDown / kExpDenominator;
|
||||
- if (again_ - next < 1)
|
||||
- again_ -= 1;
|
||||
+ if (again_ - next < againMinStep_)
|
||||
+ again_ -= againMinStep_;
|
||||
else
|
||||
again_ = next;
|
||||
} else {
|
||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
|
||||
index c3ebb7b7..7e932a14 100644
|
||||
--- a/src/libcamera/pipeline/simple/simple.cpp
|
||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
|
||||
@@ -525,7 +525,7 @@ int SimpleCameraData::init()
|
||||
* Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
|
||||
*/
|
||||
if (!converter_ && pipe->swIspEnabled()) {
|
||||
- swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
|
||||
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
|
||||
if (!swIsp_->isValid()) {
|
||||
LOG(SimplePipeline, Warning)
|
||||
<< "Failed to create software ISP, disabling software debayering";
|
||||
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
|
||||
index 9b49be41..ea4d96e4 100644
|
||||
--- a/src/libcamera/software_isp/software_isp.cpp
|
||||
+++ b/src/libcamera/software_isp/software_isp.cpp
|
||||
@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
|
||||
/**
|
||||
* \brief Constructs SoftwareIsp object
|
||||
* \param[in] pipe The pipeline handler in use
|
||||
- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
|
||||
+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler
|
||||
*/
|
||||
-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
|
||||
+SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
|
||||
: debayer_(nullptr),
|
||||
debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
|
||||
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
|
||||
@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont
|
||||
return;
|
||||
}
|
||||
|
||||
- int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
|
||||
+ int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() },
|
||||
debayer_->getStatsFD(),
|
||||
sharedParams_.fd(),
|
||||
- sensorControls);
|
||||
+ sensor->controls());
|
||||
if (ret) {
|
||||
LOG(SoftwareIsp, Error) << "IPA init failed";
|
||||
debayer_.reset();
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
From eb45bdfe66af7844a779bc6fcf923cd951336309 Mon Sep 17 00:00:00 2001
|
||||
From: Dennis Bonke <admin@dennisbonke.com>
|
||||
Date: Fri, 6 Oct 2023 10:39:45 +0200
|
||||
Subject: [PATCH 19/25] libcamera: pipeline: simple: Enable simplepipeline for
|
||||
intel-ipu6 DNU
|
||||
|
||||
Do Not Upstream, first the ipu6 CSI receiver code needs to land in
|
||||
the kernel.
|
||||
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/pipeline/simple/simple.cpp | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
|
||||
index c76510c2..130843cd 100644
|
||||
--- a/src/libcamera/pipeline/simple/simple.cpp
|
||||
+++ b/src/libcamera/pipeline/simple/simple.cpp
|
||||
@@ -197,6 +197,7 @@ static const SimplePipelineInfo supportedDevices[] = {
|
||||
{ "mxc-isi", {} },
|
||||
{ "qcom-camss", {} },
|
||||
{ "sun6i-csi", {} },
|
||||
+ { "intel-ipu6", {} },
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
From 9bec33e5c7e6765734eeef2d22d7f7f65dee2264 Mon Sep 17 00:00:00 2001
|
||||
From 2bde6e420571c6dc0ff25246620b4c987987f6be Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Tue, 19 Dec 2023 15:45:51 +0100
|
||||
Subject: [PATCH 24/25] ov01a1s HACK
|
||||
Subject: [PATCH 20/21] ov01a1s HACK
|
||||
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/camera_sensor.cpp | 6 ++++++
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 5 +++++
|
||||
3 files changed, 19 insertions(+)
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 4 ++++
|
||||
3 files changed, 18 insertions(+)
|
||||
|
||||
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
|
||||
index f19f72ea..7ad4b9ef 100644
|
||||
|
@ -35,19 +35,18 @@ index f19f72ea..7ad4b9ef 100644
|
|||
int ret = generateId();
|
||||
if (ret)
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index 41c8805f..b6393925 100644
|
||||
index 3be3cdfe..d6599805 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -23,6 +23,8 @@
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
+extern bool is_ov01a1s;
|
||||
+
|
||||
DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
|
||||
: stats_(std::move(stats)), gamma_correction_(1.0)
|
||||
{
|
||||
@@ -471,6 +473,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
/**
|
||||
* \class DebayerCpu
|
||||
* \brief Class for debayering on the CPU
|
||||
@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputFormat);
|
||||
|
||||
|
@ -57,39 +56,40 @@ index 41c8805f..b6393925 100644
|
|||
if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
|
||||
bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
isStandardBayerOrder(bayerFormat.order)) {
|
||||
@@ -548,6 +553,9 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
|
||||
@@ -330,7 +334,11 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
|
||||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputFormat);
|
||||
|
||||
+ if (is_ov01a1s)
|
||||
+ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
|
||||
+
|
||||
xShift_ = 0;
|
||||
+
|
||||
swapRedBlueGains_ = false;
|
||||
|
||||
switch (outputFormat) {
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
index 96e21be5..503ce799 100644
|
||||
index be310f56..cda1894a 100644
|
||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
@@ -19,6 +19,8 @@
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
+extern bool is_ov01a1s;
|
||||
+
|
||||
SwStatsCpu::SwStatsCpu()
|
||||
: SwStats()
|
||||
{
|
||||
@@ -301,6 +303,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
/**
|
||||
* \class SwStatsCpu
|
||||
* \brief Class for gathering statistics on the CPU
|
||||
@@ -271,6 +272,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
BayerFormat bayerFormat =
|
||||
BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
|
||||
|
||||
+ if (is_ov01a1s)
|
||||
+ bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
|
||||
+
|
||||
startFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::resetStats;
|
||||
finishFrame_ = (SwStats::statsVoidFn)&SwStatsCpu::finishStats;
|
||||
|
||||
if (bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
setupStandardBayerOrder(bayerFormat.order) == 0) {
|
||||
switch (bayerFormat.bitDepth) {
|
||||
--
|
||||
2.43.0
|
||||
2.43.2
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
From a21bb26dcfcc00425f031421b87576f9c81e4824 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Wed, 24 Jan 2024 20:44:29 +0100
|
||||
Subject: [PATCH 21/21] libcamera: debayer_cpu: Make the minimum size 1280x720
|
||||
|
||||
pipewire + firefox default to what looks like 640x480 if we export
|
||||
the entire supported cropping range. Hardcode 720p as minsize for now.
|
||||
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index d6599805..5a06b191 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -790,10 +790,17 @@ SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
|
||||
return {};
|
||||
}
|
||||
|
||||
- return SizeRange(Size(pattern_size.width, pattern_size.height),
|
||||
- Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
|
||||
- (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
|
||||
- pattern_size.width, pattern_size.height);
|
||||
+ /*
|
||||
+ * pipewire + firefox default to what looks like 640x480
|
||||
+ * if we export the entire supported cropping range.
|
||||
+ * Hardcode 720p as minsize for now. Minsize should be
|
||||
+ * Size(pattern_size.width, pattern_size.height)
|
||||
+ */
|
||||
+ unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1);
|
||||
+ unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1);
|
||||
+ return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)),
|
||||
+ Size(w, h),
|
||||
+ pattern_size.width, pattern_size.height);
|
||||
}
|
||||
|
||||
} /* namespace libcamera */
|
||||
--
|
||||
2.43.2
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
From f939e68a3ef556e572f0140df6d7ef17d72f457e Mon Sep 17 00:00:00 2001
|
||||
From: Marttico <g.martti@gmail.com>
|
||||
Date: Wed, 20 Dec 2023 20:26:15 +0100
|
||||
Subject: [PATCH 21/25] libcamera: swstats_cpu: Add support for 10bpp
|
||||
IGIG_GBGR_IGIG_GRGB input
|
||||
|
||||
Add support to SwStatsCpu for 10bpp IGIG_GBGR_IGIG_GRGB input
|
||||
generated by the Omnivision ov01a1s sensor.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/swstats_cpu.h | 3 +
|
||||
src/libcamera/software_isp/swstats_cpu.cpp | 76 +++++++++++++++++++
|
||||
2 files changed, 79 insertions(+)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
index e7abc6bb..a47241e1 100644
|
||||
--- a/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
+++ b/include/libcamera/internal/software_isp/swstats_cpu.h
|
||||
@@ -42,6 +42,9 @@ private:
|
||||
/* Bayer 10 bpp packed */
|
||||
void statsBGGR10PLine0(const uint8_t *src[]);
|
||||
void statsGBRG10PLine0(const uint8_t *src[]);
|
||||
+ /* IGIG_GBGR_IGIG_GRGB 10 bpp unpacked */
|
||||
+ void statsRGBIR10Line0(const uint8_t *src[]);
|
||||
+ void statsRGBIR10Line2(const uint8_t *src[]);
|
||||
void resetStats(void);
|
||||
void finishStats(void);
|
||||
|
||||
diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
index 87550371..96e21be5 100644
|
||||
--- a/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/swstats_cpu.cpp
|
||||
@@ -187,6 +187,68 @@ void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
|
||||
statsBayer10P(window_.width, src0, src1, false, stats_);
|
||||
}
|
||||
|
||||
+void SwStatsCpu::statsRGBIR10Line0(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *src0_16 = (const uint16_t *)src[2] + window_.x;
|
||||
+ const uint16_t *src1_16 = (const uint16_t *)src[3] + window_.x;
|
||||
+ uint16_t g3, g4;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint16_t)
|
||||
+
|
||||
+ /* x += 8 sample every other 4x4 block */
|
||||
+ for (int x = 0; x < (int)window_.width; x += 8) {
|
||||
+ /* IGIG */
|
||||
+ //i = src0_16[x];
|
||||
+ g2 = src0_16[x + 1];
|
||||
+ //i = src0_16[x + 2];
|
||||
+ g4 = src0_16[x + 3];
|
||||
+
|
||||
+ /* GBGR */
|
||||
+ g = src1_16[x];
|
||||
+ b = src1_16[x + 1];
|
||||
+ g3 = src1_16[x + 2];
|
||||
+ r = src1_16[x + 3];
|
||||
+
|
||||
+ g = (g + g2 + g3 + g4) / 4;
|
||||
+
|
||||
+ /* divide Y by 4 for 10 -> 8 bpp value */
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
+void SwStatsCpu::statsRGBIR10Line2(const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *src0_16 = (const uint16_t *)src[2] + window_.x;
|
||||
+ const uint16_t *src1_16 = (const uint16_t *)src[3] + window_.x;
|
||||
+ uint16_t g3, g4;
|
||||
+
|
||||
+ SWISP_LINARO_START_LINE_STATS(uint16_t)
|
||||
+
|
||||
+ /* x += 8 sample every other 4x4 block */
|
||||
+ for (int x = 0; x < (int)window_.width; x += 8) {
|
||||
+ /* IGIG */
|
||||
+ //i = src0_16[x];
|
||||
+ g2 = src0_16[x + 1];
|
||||
+ //i = src0_16[x + 2];
|
||||
+ g4 = src0_16[x + 3];
|
||||
+
|
||||
+ /* GRGB */
|
||||
+ g = src1_16[x];
|
||||
+ r = src1_16[x + 1];
|
||||
+ g3 = src1_16[x + 2];
|
||||
+ b = src1_16[x + 3];
|
||||
+
|
||||
+ g = (g + g2 + g3 + g4) / 4;
|
||||
+
|
||||
+ /* divide Y by 4 for 10 -> 8 bpp value */
|
||||
+ SWISP_LINARO_ACCUMULATE_LINE_STATS(4)
|
||||
+ }
|
||||
+
|
||||
+ SWISP_LINARO_FINISH_LINE_STATS()
|
||||
+}
|
||||
+
|
||||
void SwStatsCpu::resetStats(void)
|
||||
{
|
||||
stats_.sumR_ = 0;
|
||||
@@ -282,6 +344,20 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
|
||||
}
|
||||
}
|
||||
|
||||
+ if (bayerFormat.bitDepth == 10 &&
|
||||
+ bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
+ bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
|
||||
+ bpp_ = 16;
|
||||
+ patternSize_.height = 4;
|
||||
+ patternSize_.width = 4;
|
||||
+ y_skip_mask_ = 0x04;
|
||||
+ x_shift_ = 0;
|
||||
+ swap_lines_ = false;
|
||||
+ stats0_ = (SwStats::statsProcessFn)&SwStatsCpu::statsRGBIR10Line0;
|
||||
+ stats2_ = (SwStats::statsProcessFn)&SwStatsCpu::statsRGBIR10Line2;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
LOG(SwStats, Info)
|
||||
<< "Unsupported input format " << inputCfg.pixelFormat.toString();
|
||||
return -EINVAL;
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,315 +0,0 @@
|
|||
From e3638943a8bd3f93b8d81c3996035c60755b97f6 Mon Sep 17 00:00:00 2001
|
||||
From: Marttico <g.martti@gmail.com>
|
||||
Date: Wed, 20 Dec 2023 20:28:12 +0100
|
||||
Subject: [PATCH 22/25] libcamera: debayer_cpu: Add support for 10bpp
|
||||
IGIG_GBGR_IGIG_GRGB input
|
||||
|
||||
Add support to DebayerCpu for 10bpp IGIG_GBGR_IGIG_GRGB input
|
||||
generated by the Omnivision ov01a1s sensor.
|
||||
|
||||
Co-authored-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
|
||||
Co-authored-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Toon Langendam <t.langendam@gmail.com>
|
||||
Signed-off-by: Marttico <g.martti@gmail.com>
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
.../internal/software_isp/debayer_cpu.h | 5 +
|
||||
src/libcamera/software_isp/debayer_cpu.cpp | 251 ++++++++++++++++++
|
||||
2 files changed, 256 insertions(+)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer_cpu.h b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
index bdeab7c0..52af117f 100644
|
||||
--- a/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
+++ b/include/libcamera/internal/software_isp/debayer_cpu.h
|
||||
@@ -96,6 +96,11 @@ private:
|
||||
void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
|
||||
+ /* IGIG_GBGR_IGIG_GRGB unpacked 10-bit raw bayer format */
|
||||
+ void debayerIGIG10Line0(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayerGBGR10Line1(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayerIGIG10Line2(uint8_t *dst, const uint8_t *src[]);
|
||||
+ void debayerGRGB10Line3(uint8_t *dst, const uint8_t *src[]);
|
||||
|
||||
typedef void (DebayerCpu::*debayerFn)(uint8_t *dst, const uint8_t *src[]);
|
||||
|
||||
diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
index 0edea4d3..41c8805f 100644
|
||||
--- a/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
|
||||
@@ -228,6 +228,238 @@ void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
|
||||
}
|
||||
}
|
||||
|
||||
+void DebayerCpu::debayerIGIG10Line0(uint8_t *dst, const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *prev = (const uint16_t *)src[1];
|
||||
+ const uint16_t *curr = (const uint16_t *)src[2];
|
||||
+ const uint16_t *next = (const uint16_t *)src[3];
|
||||
+
|
||||
+ for (int x = 0; x < (int)window_.width;) {
|
||||
+ /*
|
||||
+ * IGIG line pixel 0: IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ */
|
||||
+ *dst++ = blue_[(prev[x - 1] + next[x + 1]) / 8];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(prev[x + 1] + next[x - 1]) / 8];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 1: GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ */
|
||||
+ *dst++ = blue_[next[x] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[prev[x] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 2: IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ */
|
||||
+ *dst++ = blue_[(prev[x + 1] + next[x - 1]) / 8];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(prev[x - 1] + next[x + 1]) / 8];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 3: GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ */
|
||||
+ *dst++ = blue_[prev[x] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[next[x] / 4];
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::debayerGBGR10Line1(uint8_t *dst, const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *prev2 = (const uint16_t *)src[0];
|
||||
+ const uint16_t *prev = (const uint16_t *)src[1];
|
||||
+ const uint16_t *curr = (const uint16_t *)src[2];
|
||||
+ const uint16_t *next = (const uint16_t *)src[3];
|
||||
+ const uint16_t *next2 = (const uint16_t *)src[4];
|
||||
+
|
||||
+ for (int x = 0; x < (int)window_.width;) {
|
||||
+ /*
|
||||
+ * GBGR line pixel 0: GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x + 1] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[curr[x - 1] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GBGR line pixel 1: BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x] / 4];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GBGR line pixel 2: GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x - 1] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[curr[x + 1] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GBGR line pixel 3: RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ */
|
||||
+ *dst++ = blue_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[curr[x] / 4];
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::debayerIGIG10Line2(uint8_t *dst, const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *prev = (const uint16_t *)src[1];
|
||||
+ const uint16_t *curr = (const uint16_t *)src[2];
|
||||
+ const uint16_t *next = (const uint16_t *)src[3];
|
||||
+
|
||||
+ for (int x = 0; x < (int)window_.width;) {
|
||||
+ /*
|
||||
+ * IGIG line pixel 0: IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ */
|
||||
+ *dst++ = blue_[(prev[x + 1] + next[x - 1]) / 8];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(prev[x - 1] + next[x + 1]) / 8];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 1: GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ */
|
||||
+ *dst++ = blue_[prev[x] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[next[x] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 2: IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ */
|
||||
+ *dst++ = blue_[(prev[x - 1] + next[x + 1]) / 8];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(prev[x + 1] + next[x - 1]) / 8];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * IGIG line pixel 3: GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ */
|
||||
+ *dst++ = blue_[next[x] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[prev[x] / 4];
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void DebayerCpu::debayerGRGB10Line3(uint8_t *dst, const uint8_t *src[])
|
||||
+{
|
||||
+ const uint16_t *prev2 = (const uint16_t *)src[0];
|
||||
+ const uint16_t *prev = (const uint16_t *)src[1];
|
||||
+ const uint16_t *curr = (const uint16_t *)src[2];
|
||||
+ const uint16_t *next = (const uint16_t *)src[3];
|
||||
+ const uint16_t *next2 = (const uint16_t *)src[4];
|
||||
+
|
||||
+ for (int x = 0; x < (int)window_.width;) {
|
||||
+ /*
|
||||
+ * GRGB line pixel 0: GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x - 1] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[curr[x + 1] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GRGB line pixel 1: RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ */
|
||||
+ *dst++ = blue_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[curr[x] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GRGB line pixel 2: GBGRG
|
||||
+ * IGIGI
|
||||
+ * GRGBG
|
||||
+ * IGIGI
|
||||
+ * GBGRG
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x + 1] / 4];
|
||||
+ *dst++ = green_[curr[x] / 4];
|
||||
+ *dst++ = red_[curr[x - 1] / 4];
|
||||
+ x++;
|
||||
+
|
||||
+ /*
|
||||
+ * GRGB line pixel 3: BGRGB
|
||||
+ * GIGIG
|
||||
+ * RGBGR
|
||||
+ * GIGIG
|
||||
+ * BGRGB
|
||||
+ */
|
||||
+ *dst++ = blue_[curr[x] / 4];
|
||||
+ *dst++ = green_[(curr[x - 1] + curr[x + 1] + prev[x] + next[x]) / 16];
|
||||
+ *dst++ = red_[(curr[x - 2] + curr[x + 2] + prev2[x] + next2[x]) / 16];
|
||||
+ x++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static bool isStandardBayerOrder(BayerFormat::Order order)
|
||||
{
|
||||
return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
|
||||
@@ -259,6 +491,15 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ if (bayerFormat.bitDepth == 10 && bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
+ bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
|
||||
+ config.bpp = 16;
|
||||
+ config.patternSize.height = 4;
|
||||
+ config.patternSize.width = 4;
|
||||
+ config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
LOG(Debayer, Info)
|
||||
<< "Unsupported input format " << inputFormat.toString();
|
||||
return -EINVAL;
|
||||
@@ -384,6 +625,16 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
|
||||
}
|
||||
}
|
||||
|
||||
+ if (bayerFormat.bitDepth == 10 &&
|
||||
+ bayerFormat.packing == BayerFormat::Packing::None &&
|
||||
+ bayerFormat.order == BayerFormat::IGIG_GBGR_IGIG_GRGB) {
|
||||
+ debayer0_ = &DebayerCpu::debayerIGIG10Line0;
|
||||
+ debayer1_ = &DebayerCpu::debayerGBGR10Line1;
|
||||
+ debayer2_ = &DebayerCpu::debayerIGIG10Line2;
|
||||
+ debayer3_ = &DebayerCpu::debayerGRGB10Line3;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
invalid_fmt:
|
||||
LOG(Debayer, Error) << "Unsupported input output format combination";
|
||||
return -EINVAL;
|
||||
--
|
||||
2.43.0
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
From 4f2c94ba8b7f9f4d85a1d7e03f4c5272d92c3361 Mon Sep 17 00:00:00 2001
|
||||
From: Hans de Goede <hdegoede@redhat.com>
|
||||
Date: Wed, 24 Jan 2024 20:44:29 +0100
|
||||
Subject: [PATCH 25/25] libcamera: debayer_cpu: Make the minimum size 1280x720
|
||||
|
||||
pipewire + firefox default to what looks like 640x480 if we export
|
||||
the entire supported cropping range. Hardcode 720p as minsize for now.
|
||||
|
||||
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||||
---
|
||||
include/libcamera/internal/software_isp/debayer.h | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/include/libcamera/internal/software_isp/debayer.h b/include/libcamera/internal/software_isp/debayer.h
|
||||
index 39e6f393..4348173d 100644
|
||||
--- a/include/libcamera/internal/software_isp/debayer.h
|
||||
+++ b/include/libcamera/internal/software_isp/debayer.h
|
||||
@@ -112,9 +112,16 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
- return SizeRange(Size(pattern_size.width, pattern_size.height),
|
||||
- Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
|
||||
- (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1)),
|
||||
+ /*
|
||||
+ * pipewire + firefox default to what looks like 640x480
|
||||
+ * if we export the entire supported cropping range.
|
||||
+ * Hardcode 720p as minsize for now. Minsize should be
|
||||
+ * Size(pattern_size.width, pattern_size.height)
|
||||
+ */
|
||||
+ unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1);
|
||||
+ unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1);
|
||||
+ return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)),
|
||||
+ Size(w, h),
|
||||
pattern_size.width, pattern_size.height);
|
||||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
Loading…
Reference in a new issue