diff --git a/piphoto.cc b/piphoto.cc index b9ff0e6..ec3c8b1 100644 --- a/piphoto.cc +++ b/piphoto.cc @@ -18,23 +18,107 @@ using string_view = experimental::string_view; std::string ReadFile(const std::string& filename); void WriteFile(const std::string& filename, const std::string& contents); + +struct Pixel { + // 32-bit for compiler convenience, but values are 16-bit + uint32_t r; + uint32_t g; + uint32_t b; + + uint32_t Difference(const Pixel& other) const; +}; + +uint32_t Pixel::Difference(const Pixel& other) const { + return ( + ((r > other.r) ? (r - other.r) : (other.r - r)) + + ((g > other.g) ? (g - other.g) : (other.g - g)) + + ((b > other.b) ? (b - other.b) : (other.b - b)) + ); +} + + +constexpr uint32_t kNumColorChecker = 24; +constexpr std::array kColorCheckerSrgb = {{ + {0x7300, 0x5200, 0x4400}, + {0xc200, 0x9600, 0x8200}, + {0x6200, 0x7a00, 0x9d00}, + {0x5700, 0x6c00, 0x4300}, + {0x8500, 0x8000, 0xb100}, + {0x6700, 0xbd00, 0xaa00}, + {0xd600, 0x7e00, 0x2c00}, + {0x5000, 0x5b00, 0xa600}, + {0xc100, 0x5a00, 0x6300}, + {0x5e00, 0x3c00, 0x6c00}, + {0x9d00, 0xbc00, 0x4000}, + {0xe000, 0xa300, 0x2e00}, + {0x3800, 0x3d00, 0x9600}, + {0x4600, 0x9400, 0x4900}, + {0xaf00, 0x3600, 0x3c00}, + {0xe700, 0xc700, 0x1f00}, + {0xbb00, 0x5600, 0x9500}, + {0x0800, 0x8500, 0xa100}, + {0xf300, 0xf300, 0xf200}, + {0xc800, 0xc800, 0xc800}, + {0xa000, 0xa000, 0xa000}, + {0x7a00, 0x7a00, 0x7900}, + {0x5500, 0x5500, 0x5500}, + {0x3400, 0x3400, 0x3400}, +}}; + + +struct Coord { + uint32_t x; + uint32_t y; +}; + +std::ostream& operator<<(std::ostream& os, const Coord& coord); + +std::ostream& operator<<(std::ostream& os, const Coord& coord) { + return os << "(" << coord.x << ", " << coord.y << ")"; +} + +template +struct Image : public std::array, Y> { + std::array ColorCheckerClosest() const; +}; + +template +std::array Image::ColorCheckerClosest() const { + std::array closest; + std::array diff; + diff.fill(UINT32_MAX); + + for (uint32_t y = 0; y < Y; ++y) { + const auto& row = this->at(y); + + for (uint32_t x = 0; x < X; ++x) { + const auto& pixel = row.at(x); + + for (uint32_t cc = 0; cc < kNumColorChecker; ++cc) { + auto pixel_diff = pixel.Difference(kColorCheckerSrgb.at(cc)); + if (pixel_diff < diff.at(cc)) { + diff.at(cc) = pixel_diff; + closest.at(cc) = {x, y}; + } + } + } + } + + return closest; +} + + template class PiRaw { public: - struct Pixel { - // 32-bit for compiler convenience, but values are 16-bit - uint32_t r; - uint32_t g; - uint32_t b; - }; - typedef std::array, Y> Image; - - PiRaw(std::unique_ptr); + PiRaw(std::unique_ptr>); static PiRaw FromJpeg(const std::string_view& jpeg); static PiRaw FromRaw(const std::string_view& raw); std::string ToPng(); + const Image& GetImage(); + private: static constexpr uint32_t kJpegHeaderBytes = 32768; static constexpr const char* kJpegHeaderMagic = "BRCM"; @@ -53,13 +137,13 @@ class PiRaw { static Chunk GetChunk(const std::string_view& raw, const uint32_t x_chunk, const uint32_t y); static Pixel CombineRaw(uint32_t y0x0, uint32_t y0x1, uint32_t y1x0, uint32_t y1x1); - std::unique_ptr image_; + std::unique_ptr> image_; }; typedef PiRaw<3280,2464,10,16,2> PiRaw2; template -PiRaw::PiRaw(std::unique_ptr image) +PiRaw::PiRaw(std::unique_ptr> image) : image_(std::move(image)) {} template @@ -77,7 +161,7 @@ PiRaw PiRaw::FromRaw(const std::string_view& raw) { assert(raw.size() == GetRawBytes()); - auto image = std::make_unique(); + auto image = std::make_unique>(); for (uint32_t y = 0, out_y = 0; y < Y; y += 2, ++out_y) { for (uint32_t x_chunk = 0, out_x = 0; x_chunk < X / kPixelsPerChunk; ++x_chunk, out_x += kPixelsPerChunk / 2) { @@ -136,7 +220,7 @@ typename PiRaw::Chunk PiRaw::GetChunk(const std::string_vi } template -typename PiRaw::Pixel PiRaw::CombineRaw(uint32_t y0x0, uint32_t y0x1, uint32_t y1x0, uint32_t y1x1) { +Pixel PiRaw::CombineRaw(uint32_t y0x0, uint32_t y0x1, uint32_t y1x0, uint32_t y1x1) { // Function is bit layout specific Pixel ret; ret.r = y1x1; @@ -182,6 +266,10 @@ std::string PiRaw::ToPng() { return ret; } +template +const Image& PiRaw::GetImage() { + return *image_; +} std::string ReadFile(const std::string& filename) { int fh = open(filename.c_str(), O_RDONLY); @@ -199,7 +287,6 @@ std::string ReadFile(const std::string& filename) { return contents; } - void WriteFile(const std::string& filename, const std::string& contents) { int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); assert(fh != -1); @@ -210,5 +297,9 @@ void WriteFile(const std::string& filename, const std::string& contents) { int main() { auto raw = PiRaw2::FromJpeg(ReadFile("test.jpg")); + auto closest = raw.GetImage().ColorCheckerClosest(); + for (uint32_t cc = 0; cc < kNumColorChecker; ++cc) { + std::cout << cc << ": " << closest.at(cc) << std::endl; + } WriteFile("test.png", raw.ToPng()); }