diff options
Diffstat (limited to 'users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch')
-rw-r--r-- | users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch | 825 |
1 files changed, 0 insertions, 825 deletions
diff --git a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch deleted file mode 100644 index f549769f2fde..000000000000 --- a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch +++ /dev/null @@ -1,825 +0,0 @@ -From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001 -From: Hans de Goede <hdegoede@redhat.com> -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 -the 4 standard bayer orders. - -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> -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> ---- - 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/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp -new file mode 100644 -index 00000000..f932362c ---- /dev/null -+++ b/src/libcamera/software_isp/debayer_cpu.cpp -@@ -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> -+ * -+ * debayer_cpu.cpp - CPU based debayering class -+ */ -+ -+#include "debayer_cpu.h" -+ -+#include <math.h> -+#include <stdlib.h> -+#include <time.h> -+ -+#include <libcamera/formats.h> -+ -+#include "libcamera/internal/bayer_format.h" -+#include "libcamera/internal/framebuffer.h" -+#include "libcamera/internal/mapped_framebuffer.h" -+ -+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) -+{ -+#ifdef __x86_64__ -+ enableInputMemcpy_ = false; -+#else -+ enableInputMemcpy_ = true; -+#endif -+ /* Initialize gamma to 1.0 curve */ -+ for (unsigned int i = 0; i < kGammaLookupSize; i++) -+ gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize); -+ -+ for (unsigned int i = 0; i < kMaxLineBuffers; i++) -+ lineBuffers_[i] = nullptr; -+} -+ -+DebayerCpu::~DebayerCpu() -+{ -+ for (unsigned int i = 0; i < kMaxLineBuffers; i++) -+ free(lineBuffers_[i]); -+} -+ -+// RGR -+// GBG -+// RGR -+#define BGGR_BGR888(p, n, div) \ -+ *dst++ = blue_[curr[x] / (div)]; \ -+ *dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))]; \ -+ *dst++ = red_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \ -+ x++; -+ -+// GBG -+// RGR -+// GBG -+#define GRBG_BGR888(p, n, div) \ -+ *dst++ = blue_[(prev[x] + next[x]) / (2 * (div))]; \ -+ *dst++ = green_[curr[x] / (div)]; \ -+ *dst++ = red_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \ -+ x++; -+ -+// GRG -+// BGB -+// GRG -+#define GBRG_BGR888(p, n, div) \ -+ *dst++ = blue_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \ -+ *dst++ = green_[curr[x] / (div)]; \ -+ *dst++ = red_[(prev[x] + next[x]) / (2 * (div))]; \ -+ x++; -+ -+// BGB -+// GRG -+// BGB -+#define RGGB_BGR888(p, n, div) \ -+ *dst++ = blue_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \ -+ *dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))]; \ -+ *dst++ = red_[curr[x] / (div)]; \ -+ x++; -+ -+void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]) -+{ -+ const int width_in_bytes = window_.width * 5 / 4; -+ const uint8_t *prev = (const uint8_t *)src[0]; -+ const uint8_t *curr = (const uint8_t *)src[1]; -+ const uint8_t *next = (const uint8_t *)src[2]; -+ -+ /* -+ * 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. -+ */ -+ for (int x = 0; x < width_in_bytes;) { -+ /* First pixel */ -+ BGGR_BGR888(2, 1, 1) -+ /* Second pixel BGGR -> GBRG */ -+ GBRG_BGR888(1, 1, 1) -+ /* 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++; -+ } -+} -+ -+void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]) -+{ -+ const int width_in_bytes = window_.width * 5 / 4; -+ const uint8_t *prev = (const uint8_t *)src[0]; -+ 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;) { -+ /* First pixel */ -+ GRBG_BGR888(2, 1, 1) -+ /* Second pixel GRBG -> RGGB */ -+ RGGB_BGR888(1, 1, 1) -+ /* 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++; -+ } -+} -+ -+void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]) -+{ -+ const int width_in_bytes = window_.width * 5 / 4; -+ const uint8_t *prev = (const uint8_t *)src[0]; -+ 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;) { -+ /* Even pixel */ -+ GBRG_BGR888(2, 1, 1) -+ /* Odd pixel GBGR -> BGGR */ -+ BGGR_BGR888(1, 1, 1) -+ /* 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++; -+ } -+} -+ -+void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]) -+{ -+ const int width_in_bytes = window_.width * 5 / 4; -+ const uint8_t *prev = (const uint8_t *)src[0]; -+ 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;) { -+ /* Even pixel */ -+ RGGB_BGR888(2, 1, 1) -+ /* 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++; -+ } -+} -+ -+static bool isStandardBayerOrder(BayerFormat::Order order) -+{ -+ return order == BayerFormat::BGGR || order == BayerFormat::GBRG || -+ 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 = -+ BayerFormat::fromPixelFormat(inputFormat); -+ -+ if (bayerFormat.bitDepth == 10 && -+ bayerFormat.packing == BayerFormat::Packing::CSI2 && -+ isStandardBayerOrder(bayerFormat.order)) { -+ config.bpp = 10; -+ config.patternSize.width = 4; /* 5 bytes per *4* pixels */ -+ config.patternSize.height = 2; -+ config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 }); -+ return 0; -+ } -+ -+ LOG(Debayer, Info) -+ << "Unsupported input format " << inputFormat.toString(); -+ return -EINVAL; -+} -+ -+int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config) -+{ -+ if (outputFormat == formats::RGB888) { -+ config.bpp = 24; -+ return 0; -+ } -+ -+ LOG(Debayer, Info) -+ << "Unsupported output format " << outputFormat.toString(); -+ return -EINVAL; -+} -+ -+/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */ -+int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat) -+{ -+ BayerFormat bayerFormat = -+ BayerFormat::fromPixelFormat(inputFormat); -+ -+ if (bayerFormat.bitDepth == 10 && -+ bayerFormat.packing == BayerFormat::Packing::CSI2) { -+ switch (bayerFormat.order) { -+ case BayerFormat::BGGR: -+ debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888; -+ return 0; -+ case BayerFormat::GBRG: -+ debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888; -+ return 0; -+ case BayerFormat::GRBG: -+ debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888; -+ return 0; -+ case BayerFormat::RGGB: -+ debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888; -+ debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888; -+ return 0; -+ default: -+ break; -+ } -+ } -+ -+ LOG(Debayer, Error) << "Unsupported input output format combination"; -+ return -EINVAL; -+} -+ -+int DebayerCpu::configure(const StreamConfiguration &inputCfg, -+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) -+{ -+ if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) -+ return -EINVAL; -+ -+ if (stats_->configure(inputCfg) != 0) -+ return -EINVAL; -+ -+ const Size &stats_pattern_size = stats_->patternSize(); -+ if (inputConfig_.patternSize.width != stats_pattern_size.width || -+ inputConfig_.patternSize.height != stats_pattern_size.height) { -+ LOG(Debayer, Error) -+ << "mismatching stats and debayer pattern sizes for " -+ << inputCfg.pixelFormat.toString(); -+ return -EINVAL; -+ } -+ -+ inputConfig_.stride = inputCfg.stride; -+ -+ if (outputCfgs.size() != 1) { -+ LOG(Debayer, Error) -+ << "Unsupported number of output streams: " -+ << outputCfgs.size(); -+ return -EINVAL; -+ } -+ -+ const StreamConfiguration &outputCfg = outputCfgs[0]; -+ SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size); -+ std::tie(outputConfig_.stride, outputConfig_.frameSize) = -+ strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size); -+ -+ if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) { -+ LOG(Debayer, Error) -+ << "Invalid output size/stride: " -+ << "\n " << outputCfg.size << " (" << outSizeRange << ")" -+ << "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")"; -+ return -EINVAL; -+ } -+ -+ if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0) -+ return -EINVAL; -+ -+ window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) & -+ ~(inputConfig_.patternSize.width - 1); -+ window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) & -+ ~(inputConfig_.patternSize.height - 1); -+ window_.width = outputCfg.size.width; -+ window_.height = outputCfg.size.height; -+ -+ /* 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++) { -+ free(lineBuffers_[i]); -+ lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_); -+ if (!lineBuffers_[i]) -+ return -ENOMEM; -+ } -+ -+ measuredFrames_ = 0; -+ frameProcessTime_ = 0; -+ -+ 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; -+ -+ if (getInputConfig(inputFormat, config) != 0) -+ return {}; -+ -+ return config.patternSize; -+} -+ -+std::vector<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat) -+{ -+ DebayerCpu::DebayerInputConfig config; -+ -+ if (getInputConfig(inputFormat, config) != 0) -+ return std::vector<PixelFormat>(); -+ -+ return config.outputFormats; -+} -+ -+std::tuple<unsigned int, unsigned int> -+DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) -+{ -+ DebayerCpu::DebayerOutputConfig config; -+ -+ if (getOutputConfig(outputFormat, config) != 0) -+ return std::make_tuple(0, 0); -+ -+ /* round up to multiple of 8 for 64 bits alignment */ -+ unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7; -+ -+ return std::make_tuple(stride, stride * size.height); -+} -+ -+void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[]) -+{ -+ const unsigned int patternHeight = inputConfig_.patternSize.height; -+ -+ if (!enableInputMemcpy_) -+ return; -+ -+ 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 */ -+ lineBufferIndex_ = patternHeight; -+} -+ -+void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src) -+{ -+ const unsigned int patternHeight = inputConfig_.patternSize.height; -+ -+ 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; -+ -+ 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) -+{ -+ 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; -+ -+ /* [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; -+ } -+} -+ -+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; -+ -+ /* [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; -+ } -+} -+ -+static inline int64_t timeDiff(timespec &after, timespec &before) -+{ -+ return (after.tv_sec - before.tv_sec) * 1000000000LL + -+ (int64_t)after.tv_nsec - (int64_t)before.tv_nsec; -+} -+ -+void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) -+{ -+ timespec frameStartTime; -+ -+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) { -+ frameStartTime = {}; -+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime); -+ } -+ -+ /* 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); -+ -+ gamma_correction_ = params.gamma; -+ } -+ -+ 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 / div, (kGammaLookupSize - 1) }); -+ red_[i] = gamma_[idx]; -+ -+ idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) }); -+ green_[i] = gamma_[idx]; -+ -+ idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) }); -+ blue_[i] = gamma_[idx]; -+ } -+ -+ /* Copy metadata from the input buffer */ -+ FrameMetadata &metadata = output->_d()->metadata(); -+ metadata.status = input->metadata().status; -+ metadata.sequence = input->metadata().sequence; -+ metadata.timestamp = input->metadata().timestamp; -+ -+ MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read); -+ MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write); -+ if (!in.isValid() || !out.isValid()) { -+ LOG(Debayer, Error) << "mmap-ing buffer(s) failed"; -+ metadata.status = FrameMetadata::FrameError; -+ return; -+ } -+ -+ stats_->startFrame(); -+ -+ if (inputConfig_.patternSize.height == 2) -+ process2(in.planes()[0].data(), out.planes()[0].data()); -+ else -+ process4(in.planes()[0].data(), out.planes()[0].data()); -+ -+ metadata.planes()[0].bytesused = out.planes()[0].size(); -+ -+ /* Measure before emitting signals */ -+ if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure && -+ ++measuredFrames_ > DebayerCpu::kFramesToSkip) { -+ timespec frameEndTime = {}; -+ clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime); -+ frameProcessTime_ += timeDiff(frameEndTime, frameStartTime); -+ if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) { -+ const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure - -+ DebayerCpu::kFramesToSkip; -+ LOG(Debayer, Info) -+ << "Processed " << measuredFrames -+ << " frames in " << frameProcessTime_ / 1000 << "us, " -+ << frameProcessTime_ / (1000 * measuredFrames) -+ << " us/frame"; -+ } -+ } -+ -+ stats_->finishFrame(); -+ outputBufferReady.emit(output); -+ 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 62095f61..71b46539 100644 ---- a/src/libcamera/software_isp/meson.build -+++ b/src/libcamera/software_isp/meson.build -@@ -9,5 +9,6 @@ endif - - libcamera_sources += files([ - 'debayer.cpp', -+ 'debayer_cpu.cpp', - 'swstats_cpu.cpp', - ]) --- -2.43.2 - |