Better color typing

This commit is contained in:
Ian Gulliver
2017-08-13 08:44:49 -07:00
parent 0ccae577eb
commit 00a1577c53
8 changed files with 87 additions and 85 deletions

View File

@@ -1,6 +1,6 @@
all: piphoto
objects = piphoto.o lut.o util.o
objects = piphoto.o color.o lut.o util.o
piphoto: $(objects) Makefile
clang-3.9 -O3 -g -Weverything -Werror --std=c++1z --stdlib=libc++ -o piphoto $(objects) -lc++ -lunwind -lpng

4
color.cc Normal file
View File

@@ -0,0 +1,4 @@
#include "color.h"
RgbColor::RgbColor(const Color<3>& src)
: Color<3>(src) {}

12
color.h
View File

@@ -10,14 +10,22 @@
constexpr int32_t kMinColor = 0;
constexpr int32_t kMaxColor = UINT16_MAX;
class ColorBase {};
template <int32_t C>
struct Color : public Array<int32_t, C> {
struct Color : public Array<int32_t, C>, public ColorBase {
constexpr int32_t AbsDiff(const Color<C>& other) const;
constexpr Color<C> Interpolate(const Color<C>& other, int32_t mul, int32_t div) const;
constexpr Color<C> Crop() const;
};
struct RgbColor : public Color<3> {};
struct RgbColor : public Color<3> {
public:
RgbColor() = default;
RgbColor(const Color<3>& src);
};
template <int32_t C>
constexpr int32_t Color<C>::AbsDiff(const Color<C>& other) const {

View File

@@ -14,7 +14,9 @@
// Maximum LUT size that has each point adjacent to at least one ColorChecker color.
typedef Lut3d<4, 3, 3> ColorCheckerLut3d;
constexpr Array<RgbColor, 24> kColorCheckerSrgb = {{{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
const Array<RgbColor, 24> kColorCheckerSrgb = {{{
{{{{{0x7300, 0x5200, 0x4400}}}}},
{{{{{0xc200, 0x9600, 0x8200}}}}},
{{{{{0x6200, 0x7a00, 0x9d00}}}}},
@@ -40,11 +42,10 @@ constexpr Array<RgbColor, 24> kColorCheckerSrgb = {{{
{{{{{0x5500, 0x5500, 0x5500}}}}},
{{{{{0x3400, 0x3400, 0x3400}}}}},
}}};
#pragma clang diagnostic pop
template <int32_t X, int32_t Y, int32_t C>
Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, C>& image) {
static_assert(C == 3);
template <int32_t X, int32_t Y>
Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, RgbColor>& image) {
Array<Coord<2>, kColorCheckerSrgb.size()> closest;
Array<int32_t, kColorCheckerSrgb.size()> diff;
diff.fill(INT32_MAX);
@@ -68,10 +69,8 @@ Array<Coord<2>, kColorCheckerSrgb.size()> FindClosest(const Image<X, Y, C>& imag
return closest;
}
template <int32_t X, int32_t Y, int32_t C>
int32_t ScoreLut(const Image<X, Y, C>& image, const LutBase& lut) {
static_assert(C == 3);
template <int32_t X, int32_t Y>
int32_t ScoreLut(const Image<X, Y, RgbColor>& image, const LutBase& lut) {
Array<int32_t, kColorCheckerSrgb.size()> diff;
diff.fill(INT32_MAX);
@@ -93,11 +92,9 @@ int32_t ScoreLut(const Image<X, Y, C>& image, const LutBase& lut) {
return std::accumulate(diff.begin(), diff.end(), 0);
}
template <int32_t X, int32_t Y, int32_t C>
std::unique_ptr<Image<X, Y, C>> HighlightClosest(const Image<X, Y, C>& image) {
static_assert(C == 3);
auto out = std::make_unique<Image<X, Y, C>>(image);
template <int32_t X, int32_t Y>
std::unique_ptr<Image<X, Y, RgbColor>> HighlightClosest(const Image<X, Y, RgbColor>& image) {
auto out = std::make_unique<Image<X, Y, RgbColor>>(image);
auto closest = FindClosest(*out);
for (int32_t cc = 0; cc < kColorCheckerSrgb.ssize(); ++cc) {
@@ -112,10 +109,8 @@ std::unique_ptr<Image<X, Y, C>> HighlightClosest(const Image<X, Y, C>& image) {
return out;
}
template <int32_t LUT_X, int32_t LUT_Y, int32_t LUT_Z, int32_t IMG_X, int32_t IMG_Y, int32_t C>
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT_Z>* lut) {
static_assert(C == 3);
template <int32_t LUT_X, int32_t LUT_Y, int32_t LUT_Z, int32_t IMG_X, int32_t IMG_Y>
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, RgbColor>& image, Lut3d<LUT_X, LUT_Y, LUT_Z>* lut) {
auto snapshot = *lut;
int32_t diff = 0;
@@ -130,7 +125,7 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT
std::cout << Coord<3>{{{{x, y, z}}}} << std::endl;
for (int32_t c = 0; c < C; ++c) {
for (int32_t c = 0; c < color.size(); ++c) {
auto& channel = color.at(c);
auto min = FindPossibleMinimum<int32_t, int32_t, 8>(
@@ -154,10 +149,8 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT
return diff;
}
template <int32_t LUT_X, int32_t IMG_X, int32_t IMG_Y, int32_t C>
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut1d<LUT_X>* lut) {
static_assert(C == 3);
template <int32_t LUT_X, int32_t IMG_X, int32_t IMG_Y>
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, RgbColor>& image, Lut1d<LUT_X>* lut) {
auto snapshot = *lut;
int32_t diff = 0;
@@ -166,7 +159,7 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut1d<LUT_X>* lut) {
std::cout << Coord<1>{{{{x}}}} << std::endl;
for (int32_t c = 0; c < C; ++c) {
for (int32_t c = 0; c < color.ssize(); ++c) {
auto& channel = color.at(c);
auto min = FindPossibleMinimum<int32_t, int32_t, 8>(

View File

@@ -2,5 +2,8 @@
#include "color.h"
constexpr RgbColor kBlack = {{{{{0x0000, 0x0000, 0x0000}}}}};
constexpr RgbColor kWhite = {{{{{0xffff, 0xffff, 0xffff}}}}};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
const RgbColor kBlack = {{{{{0x0000, 0x0000, 0x0000}}}}};
const RgbColor kWhite = {{{{{0xffff, 0xffff, 0xffff}}}}};
#pragma clang diagnostic pop

44
image.h
View File

@@ -9,57 +9,57 @@
#include "color.h"
#include "coord.h"
template <int32_t X, int32_t Y, int32_t C>
class Image : public Array<Array<Color<C>, X>, Y> {
template <int32_t X, int32_t Y, class C>
class Image : public Array<Array<C, X>, Y> {
public:
constexpr const Color<C>& GetPixel(const Coord<2>& coord) const;
constexpr const C& GetPixel(const Coord<2>& coord) const;
void SetPixel(const Coord<2>& coord, const Color<C>& color);
void DrawXLine(const Coord<2>& start, const Color<C>& color, int32_t length);
void DrawYLine(const Coord<2>& start, const Color<C>& color, int32_t length);
void DrawRectangle(const Coord<2>& start, const Color<C>& color, int32_t x_length, int32_t y_length);
void DrawSquare(const Coord<2>& start, const Color<C>& color, int32_t length);
void SetPixel(const Coord<2>& coord, const C& color);
void DrawXLine(const Coord<2>& start, const C& color, int32_t length);
void DrawYLine(const Coord<2>& start, const C& color, int32_t length);
void DrawRectangle(const Coord<2>& start, const C& color, int32_t x_length, int32_t y_length);
void DrawSquare(const Coord<2>& start, const C& color, int32_t length);
std::string ToPng();
};
template <int32_t X, int32_t Y, int32_t C>
constexpr const Color<C>& Image<X, Y, C>::GetPixel(const Coord<2>& coord) const {
template <int32_t X, int32_t Y, class C>
constexpr const C& Image<X, Y, C>::GetPixel(const Coord<2>& coord) const {
return this->at(coord.at(1)).at(coord.at(0));
}
template <int32_t X, int32_t Y, int32_t C>
void Image<X, Y, C>::SetPixel(const Coord<2>& coord, const Color<C>& color) {
template <int32_t X, int32_t Y, class C>
void Image<X, Y, C>::SetPixel(const Coord<2>& coord, const C& color) {
if (coord.at(0) >= X || coord.at(1) >= Y) {
return;
}
this->at(coord.at(1)).at(coord.at(0)) = color;
}
template <int32_t X, int32_t Y, int32_t C>
void Image<X, Y, C>::DrawXLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
template <int32_t X, int32_t Y, class C>
void Image<X, Y, C>::DrawXLine(const Coord<2>& coord, const C& color, int32_t length) {
for (int32_t x = coord.at(0); x <= coord.at(0) + length; ++x) {
SetPixel({{{{x, coord.at(1)}}}}, color);
}
}
template <int32_t X, int32_t Y, int32_t C>
void Image<X, Y, C>::DrawYLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
template <int32_t X, int32_t Y, class C>
void Image<X, Y, C>::DrawYLine(const Coord<2>& coord, const C& color, int32_t length) {
for (int32_t y = coord.at(1); y <= coord.at(1) + length; ++y) {
SetPixel({{{{coord.at(0), y}}}}, color);
}
}
template <int32_t X, int32_t Y, int32_t C>
void Image<X, Y, C>::DrawRectangle(const Coord<2>& start, const Color<C>& color, int32_t x_length, int32_t y_length) {
template <int32_t X, int32_t Y, class C>
void Image<X, Y, C>::DrawRectangle(const Coord<2>& start, const C& color, int32_t x_length, int32_t y_length) {
DrawXLine(start, color, x_length);
DrawXLine({{{{start.at(0), start.at(1) + y_length}}}}, color, x_length);
DrawYLine(start, color, y_length);
DrawYLine({{{{start.at(0) + x_length, start.at(1)}}}}, color, y_length);
}
template <int32_t X, int32_t Y, int32_t C>
void Image<X, Y, C>::DrawSquare(const Coord<2>& start, const Color<C>& color, int32_t length) {
template <int32_t X, int32_t Y, class C>
void Image<X, Y, C>::DrawSquare(const Coord<2>& start, const C& color, int32_t length) {
DrawRectangle(start, color, length, length);
}
@@ -68,9 +68,9 @@ static inline void WriteCallback(png_structp png_ptr, png_bytep data, png_size_t
dest->append(reinterpret_cast<char*>(data), length);
}
template <int32_t X, int32_t Y, int32_t C>
template <int32_t X, int32_t Y, class C>
std::string Image<X, Y, C>::ToPng() {
static_assert(C == 3); // PNG only supports RGB
// TODO: specialize this to RgbColor
std::string ret;

5
lut.h
View File

@@ -11,9 +11,10 @@ class LutBase {
LutBase(const LutBase&) = default;
virtual ~LutBase();
// TODO: Allow other color dimensions
virtual Color<3> MapColor(const Color<3>& in) const = 0;
template <int32_t X, int32_t Y, int32_t C>
template <int32_t X, int32_t Y, class C>
std::unique_ptr<Image<X, Y, C>> MapImage(const Image<X, Y, C>& in) const;
protected:
@@ -21,7 +22,7 @@ class LutBase {
static constexpr int32_t BlockSize(int32_t points);
};
template <int32_t X, int32_t Y, int32_t C>
template <int32_t X, int32_t Y, class C>
std::unique_ptr<Image<X, Y, C>> LutBase::MapImage(const Image<X, Y, C>& in) const {
auto out = std::make_unique<Image<X, Y, C>>();

57
piraw.h
View File

@@ -10,15 +10,15 @@ namespace std {
using string_view = experimental::string_view;
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
class PiRaw {
public:
PiRaw() = delete;
PiRaw(const PiRaw&) = delete;
PiRaw(PiRaw&&) = delete;
static std::unique_ptr<Image<X / 2, Y / 2, C>> FromJpeg(const std::string_view& jpeg);
static std::unique_ptr<Image<X / 2, Y / 2, C>> FromRaw(const std::string_view& raw);
static std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> FromJpeg(const std::string_view& jpeg);
static std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> FromRaw(const std::string_view& raw);
private:
static constexpr int32_t kJpegHeaderBytes = 32768;
@@ -36,30 +36,27 @@ class PiRaw {
typedef Array<int32_t, kPixelsPerChunk> Chunk;
static constexpr Chunk GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y);
static constexpr Color<C> CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1);
static constexpr RgbColor CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1);
};
typedef PiRaw<3280, 2464, 3, 10, 16, 2> PiRaw2;
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromJpeg(const std::string_view& jpeg) {
static_assert(C == 3);
typedef PiRaw<3280, 2464, 10, 16, 2> PiRaw2;
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
typename std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> PiRaw<X, Y, D, A, P>::FromJpeg(const std::string_view& jpeg) {
size_t container_len = GetRawBytes() + kJpegHeaderBytes;
assert(jpeg.substr(jpeg.size() - container_len, 4) == kJpegHeaderMagic);
return FromRaw(jpeg.substr(jpeg.size() - GetRawBytes(), GetRawBytes()));
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromRaw(const std::string_view& raw) {
static_assert(C == 3);
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
typename std::unique_ptr<Image<X / 2, Y / 2, RgbColor>> PiRaw<X, Y, D, A, P>::FromRaw(const std::string_view& raw) {
static_assert(X % 2 == 0);
static_assert(Y % 2 == 0);
static_assert(kPixelsPerChunk == 4);
assert(raw.size() == GetRawBytes());
auto image = std::make_unique<Image<X / 2, Y / 2, C>>();
auto image = std::make_unique<Image<X / 2, Y / 2, RgbColor>>();
for (int32_t y = 0, out_y = 0; y < Y; y += 2, ++out_y) {
for (int32_t x_chunk = 0, out_x = 0; x_chunk < X / kPixelsPerChunk; ++x_chunk, out_x += kPixelsPerChunk / 2) {
@@ -72,35 +69,34 @@ typename std::unique_ptr<Image<X / 2, Y / 2, C>> PiRaw<X, Y, C, D, A, P>::FromRa
return image;
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetRawBytes() {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, D, A, P>::GetRawBytes() {
return GetRowBytes() * GetNumRows();
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetRowBytes() {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, D, A, P>::GetRowBytes() {
return Align(Align(X + P) * D / kBitsPerByte);
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetNumRows() {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, D, A, P>::GetNumRows() {
return Align(Y + P);
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, C, D, A, P>::GetChunkBytes() {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, D, A, P>::GetChunkBytes() {
return D * kPixelsPerChunk / kBitsPerByte;
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, C, D, A, P>::Align(int32_t val) {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr int32_t PiRaw<X, Y, D, A, P>::Align(int32_t val) {
return (~(A - 1)) & ((val) + (A - 1));
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr typename PiRaw<X, Y, C, D, A, P>::Chunk PiRaw<X, Y, C, D, A, P>::GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y) {
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr typename PiRaw<X, Y, D, A, P>::Chunk PiRaw<X, Y, D, A, P>::GetChunk(const std::string_view& raw, const int32_t x_chunk, const int32_t y) {
// Function is bit depth & layout specific
static_assert(C == 3);
static_assert(D == 10);
size_t start = static_cast<size_t>(y * GetRowBytes() + x_chunk * GetChunkBytes());
@@ -118,12 +114,9 @@ constexpr typename PiRaw<X, Y, C, D, A, P>::Chunk PiRaw<X, Y, C, D, A, P>::GetCh
return ret;
}
template <int32_t X, int32_t Y, int32_t C, int32_t D, int32_t A, int32_t P>
constexpr Color<C> PiRaw<X, Y, C, D, A, P>::CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1) {
// Function is bit layout specific
static_assert(C == 3);
Color<C> ret;
template <int32_t X, int32_t Y, int32_t D, int32_t A, int32_t P>
constexpr RgbColor PiRaw<X, Y, D, A, P>::CombineRaw(int32_t y0x0, int32_t y0x1, int32_t y1x0, int32_t y1x1) {
RgbColor ret;
ret.at(0) = static_cast<int32_t>(y1x1);
ret.at(1) = static_cast<int32_t>((y0x1 + y1x0) / 2);
ret.at(2) = static_cast<int32_t>(y0x0);