#include "fgc/RawImage.h" #include namespace fgc { const char* layoutName(PixelLayout layout) { switch (layout) { case PixelLayout::Mono8: return "Mono8"; case PixelLayout::RGB8: return "RGB8"; case PixelLayout::BayerRG8: return "BayerRG8"; case PixelLayout::BayerRG12p: return "BayerRG12p"; case PixelLayout::Mono16: return "Mono16"; case PixelLayout::Unknown: break; } return "Unknown"; } PixelLayout layoutFromName(const std::string& name) { for (PixelLayout l : {PixelLayout::Mono8, PixelLayout::RGB8, PixelLayout::BayerRG8, PixelLayout::BayerRG12p, PixelLayout::Mono16}) if (name == layoutName(l)) return l; return PixelLayout::Unknown; } bool layoutIsBayer(PixelLayout layout) { return layout == PixelLayout::BayerRG8 || layout == PixelLayout::BayerRG12p; } int layoutBits(PixelLayout layout) { switch (layout) { case PixelLayout::Mono8: case PixelLayout::RGB8: case PixelLayout::BayerRG8: return 8; case PixelLayout::BayerRG12p: return 12; case PixelLayout::Mono16: return 16; case PixelLayout::Unknown: break; } return 0; } size_t layoutFrameBytes(PixelLayout layout, uint32_t width, uint32_t height) { const size_t n = static_cast(width) * height; switch (layout) { case PixelLayout::Mono8: case PixelLayout::BayerRG8: return n; case PixelLayout::RGB8: return n * 3; case PixelLayout::BayerRG12p: return (n * 3 + 1) / 2; case PixelLayout::Mono16: return n * 2; case PixelLayout::Unknown: break; } return 0; } namespace { inline uint16_t px16(const uint8_t* in, size_t i) { uint16_t v; std::memcpy(&v, in + i * 2, sizeof v); // host order, possibly unaligned return v; } inline uint16_t px12p(const uint8_t* in, size_t i) { const uint8_t* p = in + (i >> 1) * 3; return (i & 1) == 0 ? static_cast(p[0] | ((p[1] & 0x0F) << 8)) : static_cast((p[1] >> 4) | (p[2] << 4)); } } // namespace void unpack12p(const uint8_t* in, uint16_t* out, size_t pixels) { const size_t pairs = pixels / 2; for (size_t k = 0; k < pairs; ++k, in += 3, out += 2) { out[0] = static_cast(in[0] | ((in[1] & 0x0F) << 8)); out[1] = static_cast((in[1] >> 4) | (in[2] << 4)); } if (pixels & 1) out[0] = static_cast(in[0] | ((in[1] & 0x0F) << 8)); } BrightnessSample sampleBrightness(const uint8_t* frame, size_t bytes, PixelLayout layout, uint32_t width, uint32_t height, size_t stride) { BrightnessSample s; const size_t pixels = static_cast(width) * height; if (!frame || layout == PixelLayout::Unknown || stride == 0 || bytes < layoutFrameBytes(layout, width, height)) return s; // Values per pixel (RGB8 has three); sampling every stride-th value keeps // the colour channels mixed for RGB8 just as for a mosaic. const size_t values = layout == PixelLayout::RGB8 ? pixels * 3 : pixels; double sum = 0; size_t hi = 0; for (size_t i = 0; i < values; i += stride) { double v = layout == PixelLayout::BayerRG12p ? px12p(frame, i) / 16.0 : layout == PixelLayout::Mono16 ? px16(frame, i) / 256.0 : frame[i]; sum += v; hi += v >= 250.0; ++s.samples; } if (s.samples) { s.mean = sum / s.samples; s.clipped = static_cast(hi) / s.samples; } return s; } } // namespace fgc