[v6,3/4] libcamera: software_isp: Add support for monochrome formats - CPU
diff mbox series

Message ID 20261017111036.700859-4-alain.cousinie@laposte.net
State New
Headers show
Series
  • libcamera: software_isp: Add support for raw monochrome formats
Related show

Commit Message

Alain Cousinie Oct. 17, 2026, 11:10 a.m. UTC
Signed-off-by: Alain CousiniƩ <alain.cousinie@laposte.net>
---
 src/libcamera/software_isp/debayer_cpu.cpp | 199 +++++++++++++++++++--
 src/libcamera/software_isp/debayer_cpu.h   |  19 ++
 2 files changed, 206 insertions(+), 12 deletions(-)

--
2.55.0

Patch
diff mbox series

diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
index ce8b3c647..788b39d8b 100644
--- a/src/libcamera/software_isp/debayer_cpu.cpp
+++ b/src/libcamera/software_isp/debayer_cpu.cpp
@@ -50,6 +50,7 @@  private:
 	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
 	void memcpyNextLine(const uint8_t *linePointers[]);
 	void process2(uint32_t frame, const uint8_t *src, uint8_t *dst);
+	void processMono2(uint32_t frame, const uint8_t *src, uint8_t *dst);
 	void process4(uint32_t frame, const uint8_t *src, uint8_t *dst);

 	/* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
@@ -424,6 +425,90 @@  void DebayerCpu::debayer12P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
 	}
 }

+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer8_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint8_t *cur = src[1];
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint8_t pixel_val = *cur++;
+		STORE_PIXEL(pixel_val, pixel_val, pixel_val)
+	}
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1]);
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint16_t native_val = *cur++;
+		uint8_t pixel_val = static_cast<uint8_t>(native_val >> 2);
+
+		STORE_PIXEL(pixel_val, pixel_val, pixel_val)
+	}
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1]);
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint16_t native_val = *cur++;
+		uint8_t pixel_val = static_cast<uint8_t>(native_val >> 4);
+
+		STORE_PIXEL(pixel_val, pixel_val, pixel_val)
+	}
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer10P_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint8_t *cur = src[1];
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint8_t gray0 = *cur++;
+		uint8_t gray1 = *cur++;
+		uint8_t gray2 = *cur++;
+		uint8_t gray3 = *cur++;
+		cur++;
+
+		STORE_PIXEL(gray0, gray0, gray0)
+		STORE_PIXEL(gray1, gray1, gray1)
+		STORE_PIXEL(gray2, gray2, gray2)
+		STORE_PIXEL(gray3, gray3, gray3)
+	}
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12P_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint8_t *cur = src[1];
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint8_t gray0 = *cur++;
+		uint8_t gray1 = *cur++;
+		cur++;
+
+		STORE_PIXEL(gray0, gray0, gray0)
+		STORE_PIXEL(gray1, gray1, gray1)
+	}
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer16_MONO_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+	const uint16_t *cur = reinterpret_cast<const uint16_t *>(src[1]);
+
+	for (int x = 0; x < static_cast<int>(window_.width);) {
+		uint16_t native_val = *cur++;
+		uint8_t pixel_val = static_cast<uint8_t>(native_val >> 8);
+
+		STORE_PIXEL(pixel_val, pixel_val, pixel_val)
+	}
+}
+
 /*
  * Setup the Debayer object according to the passed in parameters.
  * Return 0 on success, a negative errno value on failure
@@ -433,6 +518,7 @@  int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 {
 	BayerFormat bayerFormat =
 		BayerFormat::fromPixelFormat(inputFormat);
+
 	std::vector<PixelFormat> outputFormats = { formats::RGB888,
 						   formats::XRGB8888,
 						   formats::ARGB8888,
@@ -440,6 +526,20 @@  int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 						   formats::XBGR8888,
 						   formats::ABGR8888 };

+	if (bayerFormat.order == BayerFormat::Order::MONO) {
+		if (bayerFormat.packing == BayerFormat::Packing::None) {
+			config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+			config.patternSize.width = 2;
+			config.patternSize.height = 2;
+		} else if (bayerFormat.packing == BayerFormat::Packing::CSI2) {
+			config.bpp = bayerFormat.bitDepth;
+			config.patternSize.width = (bayerFormat.bitDepth == 10) ? 4 : 2;
+			config.patternSize.height = 2;
+		}
+		config.outputFormats = outputFormats;
+		return 0;
+	}
+
 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
 	    bayerFormat.packing == BayerFormat::Packing::None &&
 	    isStandardBayerOrder(bayerFormat.order)) {
@@ -513,8 +613,6 @@  int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat,
 				    PixelFormat outputFormat,
 				    bool ccmEnabled)
 {
-	BayerFormat bayerFormat =
-		BayerFormat::fromPixelFormat(inputFormat);
 	bool addAlphaByte = false;

 	xShift_ = 0;
@@ -525,6 +623,47 @@  int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat,
 		return -EINVAL;
 	};

+	BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat);
+
+	if (bayerFormat.order == BayerFormat::Order::MONO) {
+
+
+		/* \todo Disable CCM via IPA; force false here to prevent monochrome coloration */
+		ccmEnabled = false;
+
+		if (outputFormat == formats::XRGB8888 || outputFormat == formats::ARGB8888 ||
+		    outputFormat == formats::XBGR8888 || outputFormat == formats::ABGR8888) {
+			addAlphaByte = true;
+		}
+		if (bayerFormat.packing == BayerFormat::Packing::None) {
+			switch (bayerFormat.bitDepth) {
+			case 8:
+				SET_DEBAYER_METHODS(debayer8_MONO_BGR888, debayer8_MONO_BGR888)
+				return 0;
+			case 10:
+				SET_DEBAYER_METHODS(debayer10_MONO_BGR888, debayer10_MONO_BGR888)
+				return 0;
+			case 12:
+				SET_DEBAYER_METHODS(debayer12_MONO_BGR888, debayer12_MONO_BGR888)
+				return 0;
+			case 16:
+				SET_DEBAYER_METHODS(debayer16_MONO_BGR888, debayer16_MONO_BGR888)
+				return 0;
+			}
+		}
+		if (bayerFormat.packing == BayerFormat::Packing::CSI2) {
+			switch (bayerFormat.bitDepth) {
+			case 10:
+				SET_DEBAYER_METHODS(debayer10P_MONO_BGR888, debayer10P_MONO_BGR888)
+				return 0;
+			case 12:
+				SET_DEBAYER_METHODS(debayer12P_MONO_BGR888, debayer12P_MONO_BGR888)
+				return 0;
+			}
+		}
+		return invalidFmt();
+	}
+
 	switch (outputFormat) {
 	case formats::XRGB8888:
 	case formats::ARGB8888:
@@ -632,6 +771,9 @@  int DebayerCpu::configure(const StreamConfiguration &inputCfg,
 		return -EINVAL;
 	}

+	BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+	isMono_ = (bayerFormat.order == BayerFormat::Order::MONO);
+
 	if (stats_->configure(inputCfg, threads_.size()) != 0)
 		return -EINVAL;

@@ -682,10 +824,16 @@  int DebayerCpu::configure(const StreamConfiguration &inputCfg,
 	 */
 	params_.gamma = 1.0;

-	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);
+	/* Setup the window geometry according to the format type */
+	if (isMono_) {
+		window_.x = 0;
+		window_.y = 0;
+	} else {
+		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;

@@ -827,7 +975,9 @@  void DebayerCpuThread::process(uint32_t frame, const uint8_t *src, uint8_t *dst)
 	/* Adjust dst for yStart_ */
 	dst += yStart_ * debayer_->outputConfig_.stride;

-	if (debayer_->inputConfig_.patternSize.height == 2)
+	if (debayer_->isMono_)
+		processMono2(frame, src, dst);
+	else if (debayer_->inputConfig_.patternSize.height == 2)
 		process2(frame, src, dst);
 	else
 		process4(frame, src, dst);
@@ -904,6 +1054,27 @@  void DebayerCpuThread::process2(uint32_t frame, const uint8_t *src, uint8_t *dst
 	}
 }

+void DebayerCpuThread::processMono2(uint32_t frame, const uint8_t *src, uint8_t *dst)
+{
+	unsigned int outputStride = debayer_->outputConfig_.stride;
+	unsigned int inputStride = debayer_->inputConfig_.stride;
+	/* Holds [0] previous- [1] current- [2] next-line */
+	const uint8_t *linePointers[3];
+
+	for (unsigned int y = yStart_; y < yEnd_; y += 2) {
+		linePointers[1] = src;
+		debayer_->stats_->processLine0(frame, y, linePointers, threadIndex_);
+		debayer_->debayer0(dst, linePointers);
+		src += inputStride;
+		dst += outputStride;
+
+		linePointers[1] = src;
+		debayer_->debayer1(dst, linePointers);
+		src += inputStride;
+		dst += outputStride;
+	}
+}
+
 void DebayerCpuThread::process4(uint32_t frame, const uint8_t *src, uint8_t *dst)
 {
 	unsigned int outputStride = debayer_->outputConfig_.stride;
@@ -1124,11 +1295,12 @@  void DebayerCpu::stop()
 SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
 {
 	Size patternSize = this->patternSize(inputFormat);
-	unsigned int borderHeight = patternSize.height;
-
 	if (patternSize.isNull())
 		return {};

+	/* FIX: borderHeight is now safely initialized after the isNull() check */
+	unsigned int borderHeight = patternSize.height;
+
 	/* No need for top/bottom border with a pattern height of 2 */
 	if (patternSize.height == 2)
 		borderHeight = 0;
@@ -1140,13 +1312,16 @@  SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
 	if (inputSize.width < (3 * patternSize.width) ||
 	    inputSize.height < (2 * borderHeight + patternSize.height)) {
 		LOG(Debayer, Warning)
-			<< "Input format size too small: " << inputSize.toString();
+			<< "Input format size too small: " << inputSize;
 		return {};
 	}

+	BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat);
+	bool localIsMono = (bayerFormat.order == BayerFormat::Order::MONO);
+
 	return SizeRange(Size(patternSize.width, patternSize.height),
-			 Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),
-			      (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),
+			 Size((inputSize.width - (localIsMono ? 0 : 2 * patternSize.width)) & ~(patternSize.width - 1),
+			      (inputSize.height - (localIsMono ? 0 : 2 * borderHeight)) & ~(patternSize.height - 1)),
 			 patternSize.width, patternSize.height);
 }

diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
index 2c88c9e1a..c916b3330 100644
--- a/src/libcamera/software_isp/debayer_cpu.h
+++ b/src/libcamera/software_isp/debayer_cpu.h
@@ -119,6 +119,24 @@  private:
 	void debayer12P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
 	template<bool addAlphaByte, bool ccmEnabled>
 	void debayer12P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* 8-bit raw monochrome format */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer8_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* unpacked 10-bit raw monochrome format */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer10_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* unpacked 12-bit raw monochrome format */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer12_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* CSI-2 packed 10-bit raw monochrome format */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer10P_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* CSI-2 packed 12-bit raw monochrome format */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer12P_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);
+	/* Mono 16 bpp unpacked */
+	template<bool addAlphaByte, bool ccmEnabled>
+	void debayer16_MONO_BGR888(uint8_t *dst, const uint8_t *src[]);

 	static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
 	int setupStandardBayerOrder(BayerFormat::Order order);
@@ -146,6 +164,7 @@  private:
 	std::array<double, kGammaLookupSize> gammaTable_;
 	LookupTable gammaLut_;
 	bool ccmEnabled_;
+	bool isMono_;
 	DebayerParams params_;

 	debayerFn debayer0_;