134 lines
4.9 KiB
C++
134 lines
4.9 KiB
C++
#include "fgc/RawImage.h"
|
|
|
|
#include <cstring>
|
|
|
|
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::BayerRG12Packed: return "BayerRG12Packed";
|
|
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::BayerRG12Packed,
|
|
PixelLayout::Mono16})
|
|
if (name == layoutName(l)) return l;
|
|
return PixelLayout::Unknown;
|
|
}
|
|
|
|
bool layoutIsBayer(PixelLayout layout) {
|
|
return layout == PixelLayout::BayerRG8 || layout == PixelLayout::BayerRG12p ||
|
|
layout == PixelLayout::BayerRG12Packed;
|
|
}
|
|
|
|
int layoutBits(PixelLayout layout) {
|
|
switch (layout) {
|
|
case PixelLayout::Mono8:
|
|
case PixelLayout::RGB8:
|
|
case PixelLayout::BayerRG8: return 8;
|
|
case PixelLayout::BayerRG12p:
|
|
case PixelLayout::BayerRG12Packed: 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<size_t>(width) * height;
|
|
switch (layout) {
|
|
case PixelLayout::Mono8:
|
|
case PixelLayout::BayerRG8: return n;
|
|
case PixelLayout::RGB8: return n * 3;
|
|
case PixelLayout::BayerRG12p:
|
|
case PixelLayout::BayerRG12Packed: 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<uint16_t>(p[0] | ((p[1] & 0x0F) << 8))
|
|
: static_cast<uint16_t>((p[1] >> 4) | (p[2] << 4));
|
|
}
|
|
|
|
inline uint16_t px12Packed(const uint8_t* in, size_t i) {
|
|
const uint8_t* p = in + (i >> 1) * 3;
|
|
return (i & 1) == 0 ? static_cast<uint16_t>((p[0] << 4) | (p[1] & 0x0F))
|
|
: static_cast<uint16_t>((p[2] << 4) | (p[1] >> 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<uint16_t>(in[0] | ((in[1] & 0x0F) << 8));
|
|
out[1] = static_cast<uint16_t>((in[1] >> 4) | (in[2] << 4));
|
|
}
|
|
if (pixels & 1) out[0] = static_cast<uint16_t>(in[0] | ((in[1] & 0x0F) << 8));
|
|
}
|
|
|
|
void unpack12Packed(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<uint16_t>((in[0] << 4) | (in[1] & 0x0F));
|
|
out[1] = static_cast<uint16_t>((in[2] << 4) | (in[1] >> 4));
|
|
}
|
|
if (pixels & 1) out[0] = static_cast<uint16_t>((in[0] << 4) | (in[1] & 0x0F));
|
|
}
|
|
|
|
void unpack12(PixelLayout layout, const uint8_t* in, uint16_t* out, size_t pixels) {
|
|
if (layout == PixelLayout::BayerRG12Packed) unpack12Packed(in, out, pixels);
|
|
else unpack12p(in, out, pixels);
|
|
}
|
|
|
|
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<size_t>(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::BayerRG12Packed ? px12Packed(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<double>(hi) / s.samples;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
} // namespace fgc
|