@@ -98,6 +98,9 @@ private:
/* Bayer 10 bpp packed */
void statsBGGR10PLine0(const uint8_t *src[], SwIspStats &stats);
void statsGBRG10PLine0(const uint8_t *src[], SwIspStats &stats);
+ /* Bayer 12 bpp packed */
+ void statsBGGR12PLine0(const uint8_t *src[], SwIspStats &stats);
+ void statsGBRG12PLine0(const uint8_t *src[], SwIspStats &stats);
void processBayerFrame2(MappedFrameBuffer &in);
@@ -351,6 +351,78 @@ void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
}
}
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 3 / 2;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ BGGR_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG */
+ GBRG_BGR888(1, 2, 1)
+ /* Skip 3rd src byte with 2 x 4 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 3 / 2;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ GRBG_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG */
+ RGGB_BGR888(1, 2, 1)
+ /* Skip 3rd src byte with 2 x 4 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 3 / 2;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ GBRG_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG */
+ BGGR_BGR888(1, 2, 1)
+ /* Skip 3rd src byte with 2 x 4 least-significant-bits */
+ x++;
+ }
+}
+
+template<bool addAlphaByte, bool ccmEnabled>
+void DebayerCpu::debayer12P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
+{
+ const int widthInBytes = window_.width * 3 / 2;
+ const uint8_t *prev = src[0];
+ const uint8_t *curr = src[1];
+ const uint8_t *next = src[2];
+
+ for (int x = 0; x < widthInBytes;) {
+ /* Even pixel */
+ RGGB_BGR888(2, 1, 1)
+ /* Odd pixel RGGB -> GRBG */
+ GRBG_BGR888(1, 2, 1)
+ /* Skip 3rd src byte with 2 x 4 least-significant-bits */
+ x++;
+ }
+}
+
/*
* Setup the Debayer object according to the passed in parameters.
* Return 0 on success, a negative errno value on failure
@@ -391,6 +463,21 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
return 0;
}
+ if (bayerFormat.bitDepth == 12 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = 12;
+ config.patternSize.width = 2; /* 3 bytes per *2* pixels */
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::RGB888,
+ formats::XRGB8888,
+ formats::ARGB8888,
+ formats::BGR888,
+ formats::XBGR8888,
+ formats::ABGR8888 });
+ return 0;
+ }
+
LOG(Debayer, Info)
<< "Unsupported input format " << inputFormat.toString();
return -EINVAL;
@@ -538,6 +625,26 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat,
}
}
+ if (bayerFormat.bitDepth == 12 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ SET_DEBAYER_METHODS(debayer12P_BGBG_BGR888, debayer12P_GRGR_BGR888)
+ return 0;
+ case BayerFormat::GBRG:
+ SET_DEBAYER_METHODS(debayer12P_GBGB_BGR888, debayer12P_RGRG_BGR888)
+ return 0;
+ case BayerFormat::GRBG:
+ SET_DEBAYER_METHODS(debayer12P_GRGR_BGR888, debayer12P_BGBG_BGR888)
+ return 0;
+ case BayerFormat::RGGB:
+ SET_DEBAYER_METHODS(debayer12P_RGRG_BGR888, debayer12P_GBGB_BGR888)
+ return 0;
+ default:
+ break;
+ }
+ }
+
return invalidFmt();
}
@@ -110,6 +110,15 @@ private:
void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
template<bool addAlphaByte, bool ccmEnabled>
void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ /* CSI-2 packed 12-bit raw bayer format (all the 4 orders) */
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer12P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ void debayer12P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ template<bool addAlphaByte, bool ccmEnabled>
+ 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[]);
static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
static int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
@@ -78,6 +78,19 @@ int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
return 0;
}
+ if (bayerFormat.bitDepth == 12 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = 12;
+ config.patternSize.width = 2; /* 3 bytes per *2* pixels */
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::XRGB8888,
+ formats::ARGB8888,
+ formats::XBGR8888,
+ formats::ABGR8888 });
+ return 0;
+ }
+
LOG(Debayer, Error)
<< "Unsupported input format " << inputFormat;
@@ -323,6 +323,58 @@ void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[], SwIspStats &stats)
SWSTATS_FINISH_LINE_STATS()
}
+void SwStatsCpu::statsBGGR12PLine0(const uint8_t *src[], SwIspStats &stats)
+{
+ const uint8_t *src0 = src[1] + window_.x * 3 / 2;
+ const uint8_t *src1 = src[2] + window_.x * 3 / 2;
+ const unsigned int widthInBytes = window_.width * 3 / 2;
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 6 sample every other 2x2 block */
+ for (unsigned int x = 0; x < widthInBytes; x += 6) {
+ b = src0[x];
+ g = src0[x + 1];
+ g2 = src1[x];
+ r = src1[x + 1];
+
+ g = (g + g2) / 2;
+
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
+void SwStatsCpu::statsGBRG12PLine0(const uint8_t *src[], SwIspStats &stats)
+{
+ const uint8_t *src0 = src[1] + window_.x * 3 / 2;
+ const uint8_t *src1 = src[2] + window_.x * 3 / 2;
+ const unsigned int widthInBytes = window_.width * 3 / 2;
+
+ SWSTATS_START_LINE_STATS(uint8_t)
+
+ if (swapLines_)
+ std::swap(src0, src1);
+
+ /* x += 6 sample every other 2x2 block */
+ for (unsigned int x = 0; x < widthInBytes; x += 6) {
+ g = src0[x];
+ b = src0[x + 1];
+ r = src1[x];
+ g2 = src1[x + 1];
+
+ g = (g + g2) / 2;
+
+ SWSTATS_ACCUMULATE_LINE_STATS(1)
+ }
+
+ SWSTATS_FINISH_LINE_STATS()
+}
+
/**
* \brief Reset state to start statistics gathering for a new frame
* \param[in] frame The frame number
@@ -466,6 +518,32 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg, unsigned int stat
}
}
+ if (bayerFormat.bitDepth == 12 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ patternSize_.height = 2;
+ patternSize_.width = 2; /* 3 bytes for *2* pixels */
+ /* Skip every 3th and 4th line, sample every other 2x2 block */
+ ySkipMask_ = 0x02;
+ xShift_ = 0;
+ sumShift_ = 0;
+ processFrame_ = &SwStatsCpu::processBayerFrame2;
+
+ switch (bayerFormat.order) {
+ case BayerFormat::BGGR:
+ case BayerFormat::GRBG:
+ stats0_ = &SwStatsCpu::statsBGGR12PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::GRBG;
+ return 0;
+ case BayerFormat::GBRG:
+ case BayerFormat::RGGB:
+ stats0_ = &SwStatsCpu::statsGBRG12PLine0;
+ swapLines_ = bayerFormat.order == BayerFormat::RGGB;
+ return 0;
+ default:
+ break;
+ }
+ }
+
LOG(SwStatsCpu, Info)
<< "Unsupported input format " << inputCfg.pixelFormat.toString();
return -EINVAL;
Add support for debayering 12-bit CSI2 packed raw bayer formats. Also add support for the same in SwStats, skipping one 2x2 block horizontally and skipping every 3rd and 4th line vertically, like done for other raw bayer formats. GPUISP support was always present in the packed shaders (which already handle skipping the pixel with LSBs), so enable it too. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- This is tested for RGGB12P using IMX678 on Arduino Uno Q. The sensor doesn't change bayer layout on flips, so I couldn't verify other layouts. --- .../libcamera/internal/software_isp/swstats_cpu.h | 3 + src/libcamera/software_isp/debayer_cpu.cpp | 107 +++++++++++++++++++++ src/libcamera/software_isp/debayer_cpu.h | 9 ++ src/libcamera/software_isp/debayer_egl.cpp | 13 +++ src/libcamera/software_isp/swstats_cpu.cpp | 78 +++++++++++++++ 5 files changed, 210 insertions(+) --- base-commit: 1f97c59d276922d387f7bfcfaa6d54b4084e43c1 change-id: 20260522-swisp_12p-6ad22e4d08b0 Best regards,