Group platform independent c++ code

This commit is contained in:
Ming Ming 2023-08-29 23:50:57 +08:00
parent d338d42a75
commit bca748ded3
30 changed files with 631 additions and 449 deletions

View file

@ -33,18 +33,24 @@ add_library( # Sets the name of the library.
SHARED SHARED
# Provides a relative path to your source file(s). # Provides a relative path to your source file(s).
core/filter/brightness.cpp
core/filter/color_levels.cpp
core/filter/contrast.cpp
core/filter/curve.cpp
core/filter/hslhsv.cpp
core/filter/saturation.cpp
core/filter/tint.cpp
core/filter/warmth.cpp
core/filter/yuv.cpp
core/lib/spline/spline.cpp
filter/brightness.cpp filter/brightness.cpp
filter/color_levels.cpp filter/color_levels.cpp
filter/contrast.cpp filter/contrast.cpp
filter/crop.cpp filter/crop.cpp
filter/curve.cpp
filter/hslhsv.cpp
filter/orientation.cpp filter/orientation.cpp
filter/saturation.cpp filter/saturation.cpp
filter/tint.cpp filter/tint.cpp
filter/warmth.cpp filter/warmth.cpp
filter/yuv.cpp
lib/spline/spline.cpp
arbitrary_style_transfer.cpp arbitrary_style_transfer.cpp
deep_lap_3.cpp deep_lap_3.cpp
esrgan.cpp esrgan.cpp

View file

@ -1,9 +1,3 @@
#include "exception.h"
#include "lib/base_resample.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
#include <android/asset_manager.h> #include <android/asset_manager.h>
#include <android/asset_manager_jni.h> #include <android/asset_manager_jni.h>
#include <cassert> #include <cassert>
@ -14,6 +8,13 @@
#include <tensorflow/lite/c/c_api.h> #include <tensorflow/lite/c/c_api.h>
#include <vector> #include <vector>
#include "core/lib/base_resample.h"
#include "exception.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
using namespace tflite; using namespace tflite;

View file

@ -0,0 +1,61 @@
#include <cmath>
#include <cstdint>
#include <cstring>
#include <vector>
#include "../log.h"
#include "../math_util.h"
#include "hslhsv.h"
using namespace core;
using namespace std;
namespace {
class Brightness {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Brightness";
};
} // namespace
namespace core {
namespace filter {
vector<uint8_t> applyBrightness(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return Brightness().apply(rgba8, width, height, weight);
}
} // namespace filter
} // namespace core
namespace {
vector<uint8_t> Brightness::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const float mul = 1 + weight / 2;
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto hsv = filter::rgb8ToHsv(rgba8 + p);
hsv[2] = clamp(0.f, hsv[2] * mul, 1.f);
const auto &newRgb = filter::hsvToRgb8(hsv.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace

View file

@ -0,0 +1,144 @@
#include <cstdint>
#include <vector>
#include "../log.h"
#include "../math_util.h"
using namespace core;
using namespace std;
namespace {
constexpr float INPUT_AMPLITUDE = .4f;
constexpr uint8_t OUTPUT_AMPLITUDE = 100;
class WhitePoint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static uint8_t applyInputLevel(const uint8_t p, const float weight) {
const auto pf = p / 255.f;
const auto max = 1 - weight * INPUT_AMPLITUDE;
return clamp<int>(0, clamp(0.f, pf, max) / max * 255.f, 255);
}
static uint8_t applyOutputLevel(const uint8_t p, const float weight) {
return clamp<int>(0, p / 255.f * (255 - weight * OUTPUT_AMPLITUDE), 255);
}
static std::vector<uint8_t> buildLut(const float weight);
static constexpr const char *TAG = "WhitePoint";
};
class BlackPoint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static inline uint8_t applyInputLevel(const uint8_t p, const float weight) {
const auto pf = p / 255.f;
const auto min = weight * INPUT_AMPLITUDE;
return clamp<int>(0, (clamp(min, pf, 1.f) - min) / (1 - min) * 255.f, 255);
}
static inline uint8_t applyOutputLevel(const uint8_t p, const float weight) {
const auto x = weight * OUTPUT_AMPLITUDE;
return clamp<int>(0, p / 255.f * (255 - x) + x, 255);
}
static std::vector<uint8_t> buildLut(const float weight);
static constexpr const char *TAG = "BlackPoint";
};
} // namespace
namespace core {
namespace filter {
vector<uint8_t> applyWhitePoint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return WhitePoint().apply(rgba8, width, height, weight);
}
vector<uint8_t> applyBlackPoint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return BlackPoint().apply(rgba8, width, height, weight);
}
} // namespace filter
} // namespace core
namespace {
vector<uint8_t> WhitePoint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto lut = buildLut(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> WhitePoint::buildLut(const float weight) {
vector<uint8_t> product(256);
const float weightAbs = std::abs(weight);
const auto fn =
weight > 0 ? &WhitePoint::applyInputLevel : &WhitePoint::applyOutputLevel;
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = fn(i, weightAbs);
}
return product;
}
vector<uint8_t> BlackPoint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto lut = buildLut(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> BlackPoint::buildLut(const float weight) {
vector<uint8_t> product(256);
const float weightAbs = std::abs(weight);
const auto fn =
weight > 0 ? &BlackPoint::applyInputLevel : &BlackPoint::applyOutputLevel;
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = fn(i, weightAbs);
}
return product;
}
} // namespace

View file

@ -0,0 +1,77 @@
#include <cmath>
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include "../log.h"
#include "../math_util.h"
#include "hslhsv.h"
using namespace core;
using namespace std;
namespace {
class Contrast {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Contrast";
};
inline uint8_t applySingle(const uint8_t p, const float mul) {
return clamp(0, static_cast<int>((p - 127) * mul + 127), 0xFF);
}
std::vector<uint8_t> buildLut(const float mul);
} // namespace
namespace core {
namespace filter {
vector<uint8_t> applyContrast(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return Contrast().apply(rgba8, width, height, weight);
}
} // namespace filter
} // namespace core
namespace {
vector<uint8_t> Contrast::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const float mul = weight >= 0 ? weight + 1 : (weight + 1) * .4f + .6f;
const auto lut = buildLut(mul);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> buildLut(const float mul) {
vector<uint8_t> product(256);
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = applySingle(i, mul);
}
return product;
}
} // namespace

View file

@ -3,7 +3,7 @@
#include <vector> #include <vector>
#include "../lib/spline/spline.h" #include "../lib/spline/spline.h"
#include "./curve.h" #include "curve.h"
using namespace std; using namespace std;
@ -11,23 +11,23 @@ namespace {
std::vector<uint8_t> buildLut(const vector<uint8_t> &from, std::vector<uint8_t> buildLut(const vector<uint8_t> &from,
const vector<uint8_t> &to); const vector<uint8_t> &to);
vector<double> transformPoints(const vector<uint8_t> &pts); std::vector<double> transformPoints(const vector<uint8_t> &pts);
} // namespace } // namespace
namespace plugin { namespace core {
namespace filter { namespace filter {
Curve::Curve(const vector<uint8_t> &from, const vector<uint8_t> &to) Curve::Curve(const vector<uint8_t> &from, const vector<uint8_t> &to)
: lut(buildLut(from, to)) {} : lut(buildLut(from, to)) {}
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core
namespace { namespace {
std::vector<uint8_t> buildLut(const vector<uint8_t> &from, vector<uint8_t> buildLut(const vector<uint8_t> &from,
const vector<uint8_t> &to) { const vector<uint8_t> &to) {
assert(from.size() >= 2); assert(from.size() >= 2);
assert(from[0] == 0); assert(from[0] == 0);
assert(from[from.size() - 1] == 255); assert(from[from.size() - 1] == 255);
@ -36,7 +36,7 @@ std::vector<uint8_t> buildLut(const vector<uint8_t> &from,
assert(to[to.size() - 1] == 255); assert(to[to.size() - 1] == 255);
tk::spline spline(transformPoints(from), transformPoints(to), tk::spline spline(transformPoints(from), transformPoints(to),
tk::spline::cspline_hermite); tk::spline::cspline_hermite);
std::vector<uint8_t> lut; vector<uint8_t> lut;
lut.reserve(256); lut.reserve(256);
for (int i = 0; i <= 0xFF; ++i) { for (int i = 0; i <= 0xFF; ++i) {
lut.push_back(std::min(std::max(0, static_cast<int>(spline(i))), 0xFF)); lut.push_back(std::min(std::max(0, static_cast<int>(spline(i))), 0xFF));

View file

@ -1,7 +1,7 @@
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
namespace plugin { namespace core {
namespace filter { namespace filter {
class Curve { class Curve {
@ -23,4 +23,4 @@ private:
}; };
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -0,0 +1,30 @@
#include <cstddef>
#include <cstdint>
#include <vector>
namespace core {
namespace filter {
std::vector<uint8_t> applyBrightness(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applyWhitePoint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applyBlackPoint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applyContrast(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applySaturation(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applyTint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
std::vector<uint8_t> applyWarmth(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
} // namespace filter
} // namespace core

View file

@ -4,11 +4,11 @@
#include <cstdint> #include <cstdint>
#include "../math_util.h" #include "../math_util.h"
#include "./hslhsv.h" #include "hslhsv.h"
using namespace std; using namespace std;
namespace plugin { namespace core {
namespace filter { namespace filter {
array<float, 3> rgb8ToHsl(const uint8_t *rgb8) { array<float, 3> rgb8ToHsl(const uint8_t *rgb8) {
@ -167,4 +167,4 @@ std::array<float, 3> hsvToHsl(const float *hsv) {
} }
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -3,7 +3,7 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
namespace plugin { namespace core {
namespace filter { namespace filter {
std::array<float, 3> rgb8ToHsl(const uint8_t *rgb8); std::array<float, 3> rgb8ToHsl(const uint8_t *rgb8);
@ -16,4 +16,4 @@ std::array<float, 3> hslToHsv(const float *hsl);
std::array<float, 3> hsvToHsl(const float *hsv); std::array<float, 3> hsvToHsl(const float *hsv);
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -0,0 +1,44 @@
#include <cmath>
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include "../log.h"
#include "../math_util.h"
#include "hslhsv.h"
#include "saturation.h"
using namespace std;
namespace core {
namespace filter {
vector<uint8_t> applySaturation(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return Saturation().apply(rgba8, width, height, weight);
}
vector<uint8_t> Saturation::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto hsl = filter::rgb8ToHsl(rgba8 + p);
hsl[1] = clamp(0.f, hsl[1] * (1 + weight), 1.f);
const auto &newRgb = filter::hslToRgb8(hsl.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace filter
} // namespace core

View file

@ -1,7 +1,7 @@
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
namespace plugin { namespace core {
namespace filter { namespace filter {
class Saturation { class Saturation {
@ -14,4 +14,4 @@ private:
}; };
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -0,0 +1,62 @@
#include <cstdint>
#include <cstring>
#include <exception>
#include <vector>
#include "../log.h"
#include "../math_util.h"
#include "yuv.h"
using namespace core;
using namespace std;
namespace {
class Tint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Tint";
};
} // namespace
namespace core {
namespace filter {
vector<uint8_t> applyTint(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return Tint().apply(rgba8, width, height, weight);
}
} // namespace filter
} // namespace core
namespace {
vector<uint8_t> Tint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto yuv = filter::rgb8ToYuv(rgba8 + p);
// +-0.1
yuv[1] = clamp(0.f, yuv[1] + 0.1f * weight, 1.f);
yuv[2] = clamp(0.f, yuv[2] + 0.1f * weight, 1.f);
const auto &newRgb = filter::yuvToRgb8(yuv.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace

View file

@ -0,0 +1,102 @@
#include <cmath>
#include <cstdint>
#include <memory>
#include <vector>
#include "../log.h"
#include "curve.h"
using namespace core;
using namespace std;
namespace {
class Warmth {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static filter::Curve getRCurve(const float weight);
static unique_ptr<filter::Curve> getGCurve(const float weight);
static filter::Curve getBCurve(const float weight);
static constexpr const char *TAG = "Warmth";
};
inline uint8_t weighted(const uint8_t from, const uint8_t to,
const float weight) {
return (to - from) * weight + from;
}
} // namespace
namespace core {
namespace filter {
vector<uint8_t> applyWarmth(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
return Warmth().apply(rgba8, width, height, weight);
}
} // namespace filter
} // namespace core
namespace {
vector<uint8_t> Warmth::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto rCurve = getRCurve(weight);
const auto gCurve = getGCurve(weight);
const auto bCurve = getBCurve(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = rCurve.fit(rgba8[p + 0]);
output[p + 1] = gCurve ? gCurve->fit(rgba8[p + 1]) : rgba8[p + 1];
output[p + 2] = bCurve.fit(rgba8[p + 2]);
output[p + 3] = rgba8[p + 3];
}
return output;
}
filter::Curve Warmth::getRCurve(const float weight) {
if (weight >= 0) {
return filter::Curve({0, 78, 195, 255}, {0, weighted(78, 100, weight),
weighted(195, 220, weight), 255});
} else {
return filter::Curve({0, 95, 220, 255},
{0, weighted(95, 60, std::abs(weight)),
weighted(220, 185, std::abs(weight)), 255});
}
}
unique_ptr<filter::Curve> Warmth::getGCurve(const float weight) {
if (weight >= 0) {
return make_unique<filter::Curve>(
vector<uint8_t>{0, 135, 255},
vector<uint8_t>{0, weighted(135, 125, weight), 255});
} else {
return nullptr;
}
}
filter::Curve Warmth::getBCurve(const float weight) {
if (weight >= 0) {
return filter::Curve({0, 95, 220, 255}, {0, weighted(95, 60, weight),
weighted(220, 185, weight), 255});
} else {
return filter::Curve({0, 78, 195, 255},
{0, weighted(78, 100, std::abs(weight)),
weighted(195, 220, std::abs(weight)), 255});
}
}
} // namespace

View file

@ -2,11 +2,11 @@
#include <cstdint> #include <cstdint>
#include "../math_util.h" #include "../math_util.h"
#include "./yuv.h" #include "yuv.h"
using namespace std; using namespace std;
namespace plugin { namespace core {
namespace filter { namespace filter {
array<float, 3> rgb8ToYuv(const uint8_t *rgb8) { array<float, 3> rgb8ToYuv(const uint8_t *rgb8) {
@ -37,4 +37,4 @@ array<uint8_t, 3> yuvToRgb8(const float *yuv) {
} }
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -3,7 +3,7 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
namespace plugin { namespace core {
namespace filter { namespace filter {
/** /**
@ -23,4 +23,4 @@ std::array<float, 3> rgb8ToYuv(const uint8_t *rgb8);
std::array<uint8_t, 3> yuvToRgb8(const float *yuv); std::array<uint8_t, 3> yuvToRgb8(const float *yuv);
} // namespace filter } // namespace filter
} // namespace plugin } // namespace core

View file

@ -0,0 +1,25 @@
#pragma once
#ifdef __ANDROID__
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, __VA_ARGS__)
#ifdef NDEBUG
#define LOGI(...)
#define LOGD(...)
#define LOGV(...)
#else
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, __VA_ARGS__)
#endif
#elif defined(__APPLE__)
#define LOGE(TAG, MSG, ...) printf("[TAG] MSG", __VA_ARGS__)
#define LOGW(TAG, MSG, ...) printf("[TAG] MSG", __VA_ARGS__)
#define LOGI(TAG, MSG, ...) printf("[TAG] MSG", __VA_ARGS__)
#define LOGD(TAG, MSG, ...) printf("[TAG] MSG", __VA_ARGS__)
#define LOGV(TAG, MSG, ...) printf("[TAG] MSG", __VA_ARGS__)
#endif

View file

@ -0,0 +1,11 @@
#pragma once
#include <algorithm>
namespace core {
template <typename T> inline T clamp(const T &min, const T &x, const T &max) {
return std::max(min, std::min(x, max));
}
} // namespace core

View file

@ -1,9 +1,3 @@
#include "exception.h"
#include "lib/base_resample.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
#include <RenderScriptToolkit.h> #include <RenderScriptToolkit.h>
#include <algorithm> #include <algorithm>
#include <android/asset_manager.h> #include <android/asset_manager.h>
@ -15,8 +9,15 @@
#include <jni.h> #include <jni.h>
#include <tensorflow/lite/c/c_api.h> #include <tensorflow/lite/c/c_api.h>
#include "./filter/saturation.h" #include "core/filter/filters.h"
#include "core/lib/base_resample.h"
#include "exception.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace renderscript; using namespace renderscript;
using namespace std; using namespace std;
@ -305,8 +306,8 @@ vector<uint8_t> DeepLab3ColorPop::enhance(const uint8_t *image,
// desaturate input // desaturate input
auto rgba8 = rgb8ToRgba8(image, width, height); auto rgba8 = rgb8ToRgba8(image, width, height);
vector<uint8_t> desaturate(width * height * 4); vector<uint8_t> desaturate(width * height * 4);
plugin::filter::Saturation saturation; desaturate =
desaturate = saturation.apply(rgba8.data(), width, height, -1 * weight); filter::applySaturation(rgba8.data(), width, height, -1 * weight);
// draw input on top of blurred image, with alpha map // draw input on top of blurred image, with alpha map
replaceChannel<4>(rgba8.data(), alphaFiltered.data(), width, height, 3); replaceChannel<4>(rgba8.data(), alphaFiltered.data(), width, height, 3);

View file

@ -1,32 +1,15 @@
#include <cmath>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../math_util.h"
#include "../util.h" #include "../util.h"
#include "./hslhsv.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
namespace {
class Brightness {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Brightness";
};
} // namespace
extern "C" JNIEXPORT jbyteArray JNICALL extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_nkming_nc_1photos_plugin_image_1processor_Brightness_applyNative( Java_com_nkming_nc_1photos_plugin_image_1processor_Brightness_applyNative(
JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height,
@ -38,7 +21,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Brightness_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = Brightness().apply( const auto result = filter::applyBrightness(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -49,29 +32,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Brightness_applyNative(
return nullptr; return nullptr;
} }
} }
namespace {
vector<uint8_t> Brightness::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const float mul = 1 + weight / 2;
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto hsv = filter::rgb8ToHsv(rgba8 + p);
hsv[2] = clamp(0.f, hsv[2] * mul, 1.f);
const auto &newRgb = filter::hsvToRgb8(hsv.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace

View file

@ -1,66 +1,15 @@
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../math_util.h"
#include "../util.h" #include "../util.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
namespace {
constexpr float INPUT_AMPLITUDE = .4f;
constexpr uint8_t OUTPUT_AMPLITUDE = 100;
class WhitePoint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static uint8_t applyInputLevel(const uint8_t p, const float weight) {
const auto pf = p / 255.f;
const auto max = 1 - weight * INPUT_AMPLITUDE;
return clamp<int>(0, clamp(0.f, pf, max) / max * 255.f, 255);
}
static uint8_t applyOutputLevel(const uint8_t p, const float weight) {
return clamp<int>(0, p / 255.f * (255 - weight * OUTPUT_AMPLITUDE), 255);
}
static std::vector<uint8_t> buildLut(const float weight);
static constexpr const char *TAG = "WhitePoint";
};
class BlackPoint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static inline uint8_t applyInputLevel(const uint8_t p, const float weight) {
const auto pf = p / 255.f;
const auto min = weight * INPUT_AMPLITUDE;
return clamp<int>(0, (clamp(min, pf, 1.f) - min) / (1 - min) * 255.f, 255);
}
static inline uint8_t applyOutputLevel(const uint8_t p, const float weight) {
const auto x = weight * OUTPUT_AMPLITUDE;
return clamp<int>(0, p / 255.f * (255 - x) + x, 255);
}
static std::vector<uint8_t> buildLut(const float weight);
static constexpr const char *TAG = "BlackPoint";
};
} // namespace
extern "C" JNIEXPORT jbyteArray JNICALL extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_nkming_nc_1photos_plugin_image_1processor_WhitePoint_applyNative( Java_com_nkming_nc_1photos_plugin_image_1processor_WhitePoint_applyNative(
JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height,
@ -72,7 +21,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_WhitePoint_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = WhitePoint().apply( const auto result = filter::applyWhitePoint(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -95,7 +44,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_BlackPoint_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = BlackPoint().apply( const auto result = filter::applyBlackPoint(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -106,73 +55,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_BlackPoint_applyNative(
return nullptr; return nullptr;
} }
} }
namespace {
vector<uint8_t> WhitePoint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto lut = buildLut(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> WhitePoint::buildLut(const float weight) {
vector<uint8_t> product(256);
const float weightAbs = std::abs(weight);
const auto fn =
weight > 0 ? &WhitePoint::applyInputLevel : &WhitePoint::applyOutputLevel;
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = fn(i, weightAbs);
}
return product;
}
vector<uint8_t> BlackPoint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto lut = buildLut(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> BlackPoint::buildLut(const float weight) {
vector<uint8_t> product(256);
const float weightAbs = std::abs(weight);
const auto fn =
weight > 0 ? &BlackPoint::applyInputLevel : &BlackPoint::applyOutputLevel;
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = fn(i, weightAbs);
}
return product;
}
} // namespace

View file

@ -1,39 +1,15 @@
#include <cmath>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <memory>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../math_util.h"
#include "../util.h" #include "../util.h"
#include "./hslhsv.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
namespace {
class Contrast {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Contrast";
};
inline uint8_t applySingle(const uint8_t p, const float mul) {
return clamp(0, static_cast<int>((p - 127) * mul + 127), 0xFF);
}
std::vector<uint8_t> buildLut(const float mul);
} // namespace
extern "C" JNIEXPORT jbyteArray JNICALL extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_nkming_nc_1photos_plugin_image_1processor_Contrast_applyNative( Java_com_nkming_nc_1photos_plugin_image_1processor_Contrast_applyNative(
JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height,
@ -45,7 +21,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Contrast_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = Contrast().apply( const auto result = filter::applyContrast(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -56,38 +32,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Contrast_applyNative(
return nullptr; return nullptr;
} }
} }
namespace {
vector<uint8_t> Contrast::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const float mul = weight >= 0 ? weight + 1 : (weight + 1) * .4f + .6f;
const auto lut = buildLut(mul);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = lut[rgba8[p + 0]];
output[p + 1] = lut[rgba8[p + 1]];
output[p + 2] = lut[rgba8[p + 2]];
output[p + 3] = rgba8[p + 3];
}
return output;
}
vector<uint8_t> buildLut(const float mul) {
vector<uint8_t> product(256);
#pragma omp parallel for
for (size_t i = 0; i < 256; ++i) {
product[i] = applySingle(i, mul);
}
return product;
}
} // namespace

View file

@ -1,18 +1,12 @@
#include <cmath>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <memory>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../math_util.h"
#include "../util.h" #include "../util.h"
#include "./hslhsv.h"
#include "./saturation.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
@ -27,7 +21,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Saturation_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = filter::Saturation().apply( const auto result = filter::applySaturation(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, value); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, value);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -38,30 +32,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Saturation_applyNative(
return nullptr; return nullptr;
} }
} }
namespace plugin {
namespace filter {
vector<uint8_t> Saturation::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto hsl = filter::rgb8ToHsl(rgba8 + p);
hsl[1] = clamp(0.f, hsl[1] * (1 + weight), 1.f);
const auto &newRgb = filter::hslToRgb8(hsl.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace
}

View file

@ -1,31 +1,15 @@
#include <cstdint> #include <cstdint>
#include <cstring>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../math_util.h"
#include "../util.h" #include "../util.h"
#include "./yuv.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
namespace {
class Tint {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static constexpr const char *TAG = "Tint";
};
} // namespace
extern "C" JNIEXPORT jbyteArray JNICALL extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_nkming_nc_1photos_plugin_image_1processor_Tint_applyNative( Java_com_nkming_nc_1photos_plugin_image_1processor_Tint_applyNative(
JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height,
@ -37,8 +21,8 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Tint_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = Tint().apply(reinterpret_cast<uint8_t *>(cRgba8.get()), const auto result = filter::applyTint(
width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
reinterpret_cast<const int8_t *>(result.data())); reinterpret_cast<const int8_t *>(result.data()));
@ -48,30 +32,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Tint_applyNative(
return nullptr; return nullptr;
} }
} }
namespace {
vector<uint8_t> Tint::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
auto yuv = filter::rgb8ToYuv(rgba8 + p);
// +-0.1
yuv[1] = clamp(0.f, yuv[1] + 0.1f * weight, 1.f);
yuv[2] = clamp(0.f, yuv[2] + 0.1f * weight, 1.f);
const auto &newRgb = filter::yuvToRgb8(yuv.data());
memcpy(output.data() + p, newRgb.data(), 3);
output[p + 3] = rgba8[p + 3];
}
return output;
}
} // namespace

View file

@ -1,40 +1,15 @@
#include <cmath>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <jni.h> #include <jni.h>
#include <memory>
#include <vector>
#include "../core/filter/filters.h"
#include "../exception.h" #include "../exception.h"
#include "../log.h"
#include "../util.h" #include "../util.h"
#include "./curve.h"
using namespace core;
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
namespace {
class Warmth {
public:
std::vector<uint8_t> apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight);
private:
static filter::Curve getRCurve(const float weight);
static unique_ptr<filter::Curve> getGCurve(const float weight);
static filter::Curve getBCurve(const float weight);
static constexpr const char *TAG = "Warmth";
};
inline uint8_t weighted(const uint8_t from, const uint8_t to,
const float weight) {
return (to - from) * weight + from;
}
} // namespace
extern "C" JNIEXPORT jbyteArray JNICALL extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_nkming_nc_1photos_plugin_image_1processor_Warmth_applyNative( Java_com_nkming_nc_1photos_plugin_image_1processor_Warmth_applyNative(
JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height,
@ -46,7 +21,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Warmth_applyNative(
[&](jbyte *obj) { [&](jbyte *obj) {
env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT);
}); });
const auto result = Warmth().apply( const auto result = filter::applyWarmth(
reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight); reinterpret_cast<uint8_t *>(cRgba8.get()), width, height, weight);
auto resultAry = env->NewByteArray(result.size()); auto resultAry = env->NewByteArray(result.size());
env->SetByteArrayRegion(resultAry, 0, result.size(), env->SetByteArrayRegion(resultAry, 0, result.size(),
@ -57,62 +32,3 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Warmth_applyNative(
return nullptr; return nullptr;
} }
} }
namespace {
vector<uint8_t> Warmth::apply(const uint8_t *rgba8, const size_t width,
const size_t height, const float weight) {
LOGI(TAG, "[apply] weight: %.2f", weight);
if (weight == 0) {
// shortcut
return vector<uint8_t>(rgba8, rgba8 + width * height * 4);
}
const auto rCurve = getRCurve(weight);
const auto gCurve = getGCurve(weight);
const auto bCurve = getBCurve(weight);
vector<uint8_t> output(width * height * 4);
#pragma omp parallel for
for (size_t i = 0; i < width * height; ++i) {
const auto p = i * 4;
output[p + 0] = rCurve.fit(rgba8[p + 0]);
output[p + 1] = gCurve ? gCurve->fit(rgba8[p + 1]) : rgba8[p + 1];
output[p + 2] = bCurve.fit(rgba8[p + 2]);
output[p + 3] = rgba8[p + 3];
}
return output;
}
filter::Curve Warmth::getRCurve(const float weight) {
if (weight >= 0) {
return filter::Curve({0, 78, 195, 255}, {0, weighted(78, 100, weight),
weighted(195, 220, weight), 255});
} else {
return filter::Curve({0, 95, 220, 255},
{0, weighted(95, 60, std::abs(weight)),
weighted(220, 185, std::abs(weight)), 255});
}
}
unique_ptr<filter::Curve> Warmth::getGCurve(const float weight) {
if (weight >= 0) {
return make_unique<filter::Curve>(
vector<uint8_t>{0, 135, 255},
vector<uint8_t>{0, weighted(135, 125, weight), 255});
} else {
return nullptr;
}
}
filter::Curve Warmth::getBCurve(const float weight) {
if (weight >= 0) {
return filter::Curve({0, 95, 220, 255}, {0, weighted(95, 60, weight),
weighted(220, 185, weight), 255});
} else {
return filter::Curve({0, 78, 195, 255},
{0, weighted(78, 100, std::abs(weight)),
weighted(195, 220, std::abs(weight)), 255});
}
}
} // namespace

View file

@ -1,9 +1,3 @@
#include "exception.h"
#include "lib/base_resample.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
#include <algorithm> #include <algorithm>
#include <android/asset_manager.h> #include <android/asset_manager.h>
#include <android/asset_manager_jni.h> #include <android/asset_manager_jni.h>
@ -12,6 +6,13 @@
#include <jni.h> #include <jni.h>
#include <tensorflow/lite/c/c_api.h> #include <tensorflow/lite/c/c_api.h>
#include "core/lib/base_resample.h"
#include "exception.h"
#include "log.h"
#include "stopwatch.h"
#include "tflite_wrapper.h"
#include "util.h"
using namespace plugin; using namespace plugin;
using namespace std; using namespace std;
using namespace tflite; using namespace tflite;