Skip to content

Commit

Permalink
Support compressed pixel formats when saving DNGs
Browse files Browse the repository at this point in the history
When we receive a compressed buffer we decompress it in software so
that it can be written to the DNG file.

Signed-off-by: David Plowman <[email protected]>
  • Loading branch information
davidplowman committed Oct 4, 2023
1 parent 64c8f23 commit 1a5497e
Showing 1 changed file with 172 additions and 33 deletions.
205 changes: 172 additions & 33 deletions image/dng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,47 @@ struct BayerFormat
int bits;
char const *order;
bool packed;
bool compressed;
};

static const std::map<PixelFormat, BayerFormat> bayer_formats =
{
{ formats::SRGGB10_CSI2P, { "RGGB-10", 10, TIFF_RGGB, true } },
{ formats::SGRBG10_CSI2P, { "GRBG-10", 10, TIFF_GRBG, true } },
{ formats::SBGGR10_CSI2P, { "BGGR-10", 10, TIFF_BGGR, true } },
{ formats::SGBRG10_CSI2P, { "GBRG-10", 10, TIFF_GBRG, true } },

{ formats::SRGGB10, { "RGGB-10", 10, TIFF_RGGB, false } },
{ formats::SGRBG10, { "GRBG-10", 10, TIFF_GRBG, false } },
{ formats::SBGGR10, { "BGGR-10", 10, TIFF_BGGR, false } },
{ formats::SGBRG10, { "GBRG-10", 10, TIFF_GBRG, false } },

{ formats::SRGGB12_CSI2P, { "RGGB-12", 12, TIFF_RGGB, true } },
{ formats::SGRBG12_CSI2P, { "GRBG-12", 12, TIFF_GRBG, true } },
{ formats::SBGGR12_CSI2P, { "BGGR-12", 12, TIFF_BGGR, true } },
{ formats::SGBRG12_CSI2P, { "GBRG-12", 12, TIFF_GBRG, true } },

{ formats::SRGGB12, { "RGGB-12", 12, TIFF_RGGB, false } },
{ formats::SGRBG12, { "GRBG-12", 12, TIFF_GRBG, false } },
{ formats::SBGGR12, { "BGGR-12", 12, TIFF_BGGR, false } },
{ formats::SGBRG12, { "GBRG-12", 12, TIFF_GBRG, false } },

{ formats::SRGGB16, { "RGGB-16", 16, TIFF_RGGB, false } },
{ formats::SGRBG16, { "GRBG-16", 16, TIFF_GRBG, false } },
{ formats::SBGGR16, { "BGGR-16", 16, TIFF_BGGR, false } },
{ formats::SGBRG16, { "GBRG-16", 16, TIFF_GBRG, false } },

{ formats::R10_CSI2P, { "BGGR-10", 10, TIFF_BGGR, true } },
{ formats::R10, { "BGGR-10", 10, TIFF_BGGR, false } },
{ formats::SRGGB10_CSI2P, { "RGGB-10", 10, TIFF_RGGB, true, false } },
{ formats::SGRBG10_CSI2P, { "GRBG-10", 10, TIFF_GRBG, true, false } },
{ formats::SBGGR10_CSI2P, { "BGGR-10", 10, TIFF_BGGR, true, false } },
{ formats::SGBRG10_CSI2P, { "GBRG-10", 10, TIFF_GBRG, true, false } },

{ formats::SRGGB10, { "RGGB-10", 10, TIFF_RGGB, false, false } },
{ formats::SGRBG10, { "GRBG-10", 10, TIFF_GRBG, false, false } },
{ formats::SBGGR10, { "BGGR-10", 10, TIFF_BGGR, false, false } },
{ formats::SGBRG10, { "GBRG-10", 10, TIFF_GBRG, false, false } },

{ formats::SRGGB12_CSI2P, { "RGGB-12", 12, TIFF_RGGB, true, false } },
{ formats::SGRBG12_CSI2P, { "GRBG-12", 12, TIFF_GRBG, true, false } },
{ formats::SBGGR12_CSI2P, { "BGGR-12", 12, TIFF_BGGR, true, false } },
{ formats::SGBRG12_CSI2P, { "GBRG-12", 12, TIFF_GBRG, true, false } },

{ formats::SRGGB12, { "RGGB-12", 12, TIFF_RGGB, false, false } },
{ formats::SGRBG12, { "GRBG-12", 12, TIFF_GRBG, false, false } },
{ formats::SBGGR12, { "BGGR-12", 12, TIFF_BGGR, false, false } },
{ formats::SGBRG12, { "GBRG-12", 12, TIFF_GBRG, false, false } },

{ formats::SRGGB16, { "RGGB-16", 16, TIFF_RGGB, false, false } },
{ formats::SGRBG16, { "GRBG-16", 16, TIFF_GRBG, false, false } },
{ formats::SBGGR16, { "BGGR-16", 16, TIFF_BGGR, false, false } },
{ formats::SGBRG16, { "GBRG-16", 16, TIFF_GBRG, false, false } },

{ formats::R10_CSI2P, { "BGGR-10", 10, TIFF_BGGR, true, false } },
{ formats::R10, { "BGGR-10", 10, TIFF_BGGR, false, false } },
// Currently not in the main libcamera branch
//{ formats::R12_CSI2P, { "BGGR-12", 12, TIFF_BGGR, true } },
{ formats::R12, { "BGGR-12", 12, TIFF_BGGR, false } },
{ formats::R12, { "BGGR-12", 12, TIFF_BGGR, false, false } },

/* PiSP compressed formats. */
{ formats::RGGB16_PISP_COMP1, { "RGGB-16-PISP", 16, TIFF_RGGB, false, true } },
{ formats::GRBG16_PISP_COMP1, { "GRBG-16-PISP", 16, TIFF_GRBG, false, true } },
{ formats::GBRG16_PISP_COMP1, { "GBRG-16-PISP", 16, TIFF_GBRG, false, true } },
{ formats::BGGR16_PISP_COMP1, { "BGGR-16-PISP", 16, TIFF_BGGR, false, true } },
};

static void unpack_10bit(uint8_t const *src, StreamInfo const &info, uint16_t *dest)
Expand Down Expand Up @@ -117,6 +124,129 @@ static void unpack_16bit(uint8_t const *src, StreamInfo const &info, uint16_t *d
}
}

// We always use these compression parameters.
#define COMPRESS_OFFSET 2048
#define COMPRESS_MODE 1

static uint16_t postprocess(uint16_t a)
{
if (COMPRESS_MODE & 2)
{
if (COMPRESS_MODE == 3 && a < 0x4000)
a = a >> 2;
else if (a < 0x1000)
a = a >> 4;
else if (a < 0x1800)
a = (a - 0x800) >> 3;
else if (a < 0x3000)
a = (a - 0x1000) >> 2;
else if (a < 0x6000)
a = (a - 0x2000) >> 1;
else if (a < 0xC000)
a = (a - 0x4000);
else
a = 2 * (a - 0x8000);
}

return std::min(0xFFFF, a + COMPRESS_OFFSET);
}

static uint16_t dequantize(uint16_t q, int qmode)
{
switch (qmode)
{
case 0:
return (q < 320) ? 16 * q : 32 * (q - 160);

case 1:
return 64 * q;

case 2:
return 128 * q;

default:
return (q < 94) ? 256 * q : std::min(0xFFFF, 512 * (q - 47));
}
}

static void subBlockFunction(uint16_t *d, uint32_t w)
{
int q[4];

int qmode = (w & 3);
if (qmode < 3)
{
int field0 = (w >> 2) & 511;
int field1 = (w >> 11) & 127;
int field2 = (w >> 18) & 127;
int field3 = (w >> 25) & 127;
if (qmode == 2 && field0 >= 384)
{
q[1] = field0;
q[2] = field1 + 384;
}
else
{
q[1] = (field1 >= 64) ? field0 : field0 + 64 - field1;
q[2] = (field1 >= 64) ? field0 + field1 - 64 : field0;
}
int p1 = std::max(0, q[1] - 64);
if (qmode == 2)
p1 = std::min(384, p1);
int p2 = std::max(0, q[2] - 64);
if (qmode == 2)
p2 = std::min(384, p2);
q[0] = p1 + field2;
q[3] = p2 + field3;
}
else
{
int pack0 = (w >> 2) & 32767;
int pack1 = (w >> 17) & 32767;
q[0] = (pack0 & 15) + 16 * ((pack0 >> 8) / 11);
q[1] = (pack0 >> 4) % 176;
q[2] = (pack1 & 15) + 16 * ((pack1 >> 8) / 11);
q[3] = (pack1 >> 4) % 176;
}

d[0] = dequantize(q[0], qmode);
d[2] = dequantize(q[1], qmode);
d[4] = dequantize(q[2], qmode);
d[6] = dequantize(q[3], qmode);
}

static void uncompress(uint8_t const *src, StreamInfo const &info, uint16_t *dest)
{
// In all cases, the *decompressed* image must be a multiple of 8 columns wide.
unsigned int buf_stride_pixels = (info.width + 7) & ~7;
for (unsigned int y = 0; y < info.height; ++y)
{
uint16_t *dp = dest + y * buf_stride_pixels;
uint8_t const *sp = src + y * info.stride;

for (unsigned int x = 0; x < info.width; x+=8)
{
if (COMPRESS_MODE & 1)
{
uint32_t w0 = 0, w1 = 0;
for (int b = 0; b < 4; ++b)
w0 |= (*sp++) << (b * 8);
for (int b = 0; b < 4; ++b)
w1 |= (*sp++) << (b * 8);
subBlockFunction(dp, w0);
subBlockFunction(dp + 1, w1);
for (int i = 0; i < 8; ++i, ++dp)
*dp = postprocess(*dp);
}
else
{
for (int i = 0; i < 8; ++i)
*dp++ = postprocess((*sp++) << 8);
}
}
}
}

struct Matrix
{
Matrix(float m0, float m1, float m2,
Expand Down Expand Up @@ -177,8 +307,16 @@ void dng_save(std::vector<libcamera::Span<uint8_t>> const &mem, StreamInfo const
BayerFormat const &bayer_format = it->second;
LOG(1, "Bayer format is " << bayer_format.name);

std::vector<uint16_t> buf(info.width * info.height);
if (bayer_format.packed)
// Decompression will require a buffer that's 8 pixels aligned.
unsigned int buf_stride_pixels = info.width;
unsigned int buf_stride_pixels_padded = (buf_stride_pixels + 7) & ~7;
std::vector<uint16_t> buf(buf_stride_pixels_padded * info.height);
if (bayer_format.compressed)
{
uncompress(mem[0].data(), info, &buf[0]);
buf_stride_pixels = buf_stride_pixels_padded;
}
else if (bayer_format.packed)
{
switch (bayer_format.bits)
{
Expand Down Expand Up @@ -306,8 +444,9 @@ void dng_save(std::vector<libcamera::Span<uint8_t>> const &mem, StreamInfo const
{
for (unsigned int x = 0; x < (info.width >> 4); x++)
{
unsigned int off = (y * info.width + x) << 4;
uint32_t grey = buf[off] + buf[off + 1] + buf[off + info.width] + buf[off + info.width + 1];
unsigned int off = (y * buf_stride_pixels + x) << 4;
uint32_t grey =
buf[off] + buf[off + 1] + buf[off + buf_stride_pixels] + buf[off + buf_stride_pixels + 1];
grey = (grey << 14) >> bayer_format.bits;
grey = sqrt((double)grey); // simple "gamma correction"
thumb_buf[3 * x] = thumb_buf[3 * x + 1] = thumb_buf[3 * x + 2] = grey;
Expand Down Expand Up @@ -339,7 +478,7 @@ void dng_save(std::vector<libcamera::Span<uint8_t>> const &mem, StreamInfo const

for (unsigned int y = 0; y < info.height; y++)
{
if (TIFFWriteScanline(tif, &buf[info.width * y], y, 0) != 1)
if (TIFFWriteScanline(tif, &buf[buf_stride_pixels * y], y, 0) != 1)
throw std::runtime_error("error writing DNG image data");
}

Expand Down

0 comments on commit 1a5497e

Please sign in to comment.