Switch to Lut1d
This commit is contained in:
@@ -112,7 +112,7 @@ std::unique_ptr<Image<X, Y, C>> HighlightClosest(const Image<X, Y, C>& image) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int32_t P, int32_t LUT_X, int32_t LUT_Y, int32_t LUT_Z, int32_t IMG_X, int32_t IMG_Y, int32_t C>
|
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) {
|
int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT_Z>* lut) {
|
||||||
static_assert(C == 3);
|
static_assert(C == 3);
|
||||||
|
|
||||||
@@ -153,3 +153,37 @@ int32_t OptimizeLut(const Image<IMG_X, IMG_Y, C>& image, Lut3d<LUT_X, LUT_Y, LUT
|
|||||||
|
|
||||||
return diff;
|
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);
|
||||||
|
|
||||||
|
auto snapshot = *lut;
|
||||||
|
int32_t diff = 0;
|
||||||
|
|
||||||
|
for (int32_t x = 0; x < LUT_X; ++x) {
|
||||||
|
auto& color = lut->at(x);
|
||||||
|
|
||||||
|
std::cout << Coord<1>{{{{x}}}} << std::endl;
|
||||||
|
|
||||||
|
for (int32_t c = 0; c < C; ++c) {
|
||||||
|
auto& channel = color.at(c);
|
||||||
|
|
||||||
|
auto min = FindPossibleMinimum<int32_t, int32_t, 8>(
|
||||||
|
-UINT16_MAX, UINT16_MAX * 2,
|
||||||
|
[&image, &snapshot, x, c](int32_t val) {
|
||||||
|
auto test_lut = snapshot;
|
||||||
|
test_lut.at(x).at(c) = val;
|
||||||
|
return ScoreLut(image, test_lut);
|
||||||
|
});
|
||||||
|
// Magic value of 8 is the number of points making up a square, so the number
|
||||||
|
// of points that control any given given LUT mapping.
|
||||||
|
auto new_value = Interpolate(channel, min, INT32_C(1), INT32_C(8));
|
||||||
|
std::cout << "\tC" << c << ": " << channel << " -> " << new_value << " (interpolated from " << min << ")" << std::endl;
|
||||||
|
diff += AbsDiff(channel, new_value);
|
||||||
|
channel = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|||||||
4
image.h
4
image.h
@@ -37,14 +37,14 @@ 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) {
|
void Image<X, Y, C>::DrawXLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
|
||||||
auto& row = this->at(coord.at(1));
|
auto& row = this->at(coord.at(1));
|
||||||
|
|
||||||
for (int32_t x = coord.at(0); x < std::min(X, coord.at(0) + length); ++x) {
|
for (int32_t x = coord.at(0); x < std::min(X - 1, coord.at(0) + length); ++x) {
|
||||||
row.at(x) = color;
|
row.at(x) = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t C>
|
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) {
|
void Image<X, Y, C>::DrawYLine(const Coord<2>& coord, const Color<C>& color, int32_t length) {
|
||||||
for (int32_t y = coord.at(1); y <= std::min(Y, coord.at(1) + length); ++y) {
|
for (int32_t y = coord.at(1); y <= std::min(Y - 1, coord.at(1) + length); ++y) {
|
||||||
SetPixel({{{{coord.at(0), y}}}}, color);
|
SetPixel({{{{coord.at(0), y}}}}, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
lut.h
75
lut.h
@@ -15,6 +15,10 @@ class LutBase {
|
|||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t C>
|
template <int32_t X, int32_t Y, int32_t C>
|
||||||
std::unique_ptr<Image<X, Y, C>> MapImage(const Image<X, Y, C>& in) const;
|
std::unique_ptr<Image<X, Y, C>> MapImage(const Image<X, Y, C>& in) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static constexpr std::pair<int32_t, int32_t> FindChannelRoot(int32_t value, int32_t points);
|
||||||
|
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, int32_t C>
|
||||||
@@ -31,6 +35,59 @@ std::unique_ptr<Image<X, Y, C>> LutBase::MapImage(const Image<X, Y, C>& in) cons
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int32_t LutBase::BlockSize(int32_t points) {
|
||||||
|
return (kMaxColor + 1) / (points - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::pair<int32_t, int32_t> LutBase::FindChannelRoot(int32_t value, int32_t points) {
|
||||||
|
// points - 1 is the last point index. Since we're going to find the region
|
||||||
|
// around this point by adding to the root, we need to be at least 1 less
|
||||||
|
// than that.
|
||||||
|
int32_t index = std::min(points - 2, value / BlockSize(points));
|
||||||
|
return std::make_pair(index, value - (index * BlockSize(points)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <int32_t X>
|
||||||
|
class Lut1d : public Array<Color<3>, X>, public LutBase {
|
||||||
|
public:
|
||||||
|
static Lut1d<X> Identity();
|
||||||
|
|
||||||
|
Color<3> MapColor(const Color<3>& in) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Lut1d<2> MinimalLut1d;
|
||||||
|
|
||||||
|
template <int32_t X>
|
||||||
|
Lut1d<X> Lut1d<X>::Identity() {
|
||||||
|
Lut1d<X> ret;
|
||||||
|
|
||||||
|
Color<3> color;
|
||||||
|
for (int32_t x = 0; x < X; ++x) {
|
||||||
|
color.at(0) = color.at(1) = color.at(2) = std::min(kMaxColor, BlockSize(X) * x);
|
||||||
|
ret.at(x) = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int32_t X>
|
||||||
|
Color<3> Lut1d<X>::MapColor(const Color<3>& in) const {
|
||||||
|
Color<3> ret;
|
||||||
|
|
||||||
|
for (int32_t c = 0; c < 3; ++c) {
|
||||||
|
const auto root_rem = FindChannelRoot(in.at(c), X);
|
||||||
|
const auto& root = root_rem.first;
|
||||||
|
const auto& rem = root_rem.second;
|
||||||
|
ret.at(c) = Interpolate(
|
||||||
|
this->at(root + 0).at(c),
|
||||||
|
this->at(root + 1).at(c),
|
||||||
|
rem, BlockSize(X));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t Z>
|
template <int32_t X, int32_t Y, int32_t Z>
|
||||||
class Lut3d : public Array<Array<Array<Color<3>, X>, Y>, Z>, public LutBase {
|
class Lut3d : public Array<Array<Array<Color<3>, X>, Y>, Z>, public LutBase {
|
||||||
@@ -42,12 +99,8 @@ class Lut3d : public Array<Array<Array<Color<3>, X>, Y>, Z>, public LutBase {
|
|||||||
private:
|
private:
|
||||||
// Return value is (root_indices, remainders)
|
// Return value is (root_indices, remainders)
|
||||||
constexpr static std::pair<Coord<3>, Coord<3>> FindRoot(const Color<3>& in);
|
constexpr static std::pair<Coord<3>, Coord<3>> FindRoot(const Color<3>& in);
|
||||||
constexpr static std::pair<int32_t, int32_t> FindChannelRoot(int32_t value, int32_t points);
|
|
||||||
|
|
||||||
constexpr static int32_t BlockSize(int32_t points);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Minimum size LUT
|
|
||||||
typedef Lut3d<2, 2, 2> MinimalLut3d;
|
typedef Lut3d<2, 2, 2> MinimalLut3d;
|
||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t Z>
|
template <int32_t X, int32_t Y, int32_t Z>
|
||||||
@@ -116,17 +169,3 @@ constexpr std::pair<Coord<3>, Coord<3>> Lut3d<X, Y, Z>::FindRoot(const Color<3>&
|
|||||||
{{{{root_x.second, root_y.second, root_z.second}}}},
|
{{{{root_x.second, root_y.second, root_z.second}}}},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t Z>
|
|
||||||
constexpr std::pair<int32_t, int32_t> Lut3d<X, Y, Z>::FindChannelRoot(const int32_t value, const int32_t points) {
|
|
||||||
// points - 1 is the last point index. Since we're going to fidn the cube
|
|
||||||
// around this point by adding to the root, we need to be at least 1 less
|
|
||||||
// than that.
|
|
||||||
int32_t index = std::min(points - 2, value / BlockSize(points));
|
|
||||||
return std::make_pair(index, value - (index * BlockSize(points)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int32_t X, int32_t Y, int32_t Z>
|
|
||||||
constexpr int32_t Lut3d<X, Y, Z>::BlockSize(int32_t points) {
|
|
||||||
return (kMaxColor + 1) / (points - 1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ int main() {
|
|||||||
auto image = PiRaw2::FromJpeg(ReadFile("test.jpg"));
|
auto image = PiRaw2::FromJpeg(ReadFile("test.jpg"));
|
||||||
WriteFile("start.png", HighlightClosest(*image)->ToPng());
|
WriteFile("start.png", HighlightClosest(*image)->ToPng());
|
||||||
|
|
||||||
auto lut = MinimalLut3d::Identity();
|
auto lut = MinimalLut1d::Identity();
|
||||||
std::cout << "Initial error: " << ScoreLut(*image, lut) << std::endl;
|
std::cout << "Initial error: " << ScoreLut(*image, lut) << std::endl;
|
||||||
|
|
||||||
int32_t diff = 1;
|
int32_t diff = 1;
|
||||||
while (diff) {
|
while (diff) {
|
||||||
diff = OptimizeLut<4>(*image, &lut);
|
diff = OptimizeLut(*image, &lut);
|
||||||
std::cout << "diff=" << diff << " error=" << ScoreLut(*image, lut) << std::endl;
|
std::cout << "diff=" << diff << " error=" << ScoreLut(*image, lut) << std::endl;
|
||||||
WriteFile("inter.png", HighlightClosest(*lut.MapImage(*image))->ToPng());
|
WriteFile("inter.png", HighlightClosest(*lut.MapImage(*image))->ToPng());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user