main
Brett 2025-07-14 13:54:18 -04:00
parent 8685f4ad10
commit 3ba20ba4da
10 changed files with 174 additions and 95 deletions

BIN
black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

BIN
bwgrad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -39,7 +39,7 @@ std::array<image_ipixel_t, IMAGE_DIMENSIONS * IMAGE_DIMENSIONS * 3>& get_image(b
void cleanup(); void cleanup();
std::array<image_storage_t, 3>& get_reference_image(); const std::array<image_storage_t, 3>& get_reference_image();
std::array<size_t, 3> get_best_image_index(); std::array<size_t, 3> get_best_image_index();

View File

@ -38,7 +38,7 @@ constexpr blt::i32 IMAGE_CHANNELS = 1;
constexpr blt::size_t IMAGE_SIZE = IMAGE_DIMENSIONS * IMAGE_DIMENSIONS; constexpr blt::size_t IMAGE_SIZE = IMAGE_DIMENSIONS * IMAGE_DIMENSIONS;
constexpr blt::size_t IMAGE_SIZE_CHANNELS = IMAGE_SIZE * IMAGE_CHANNELS; constexpr blt::size_t IMAGE_SIZE_CHANNELS = IMAGE_SIZE * IMAGE_CHANNELS;
constexpr blt::size_t IMAGE_SIZE_BYTES = IMAGE_SIZE_CHANNELS * sizeof(image_pixel_t); constexpr blt::size_t IMAGE_SIZE_BYTES = IMAGE_SIZE_CHANNELS * sizeof(image_ipixel_t);
struct image_storage_t struct image_storage_t
{ {

BIN
robsonsmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
sad cannon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
sicky.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -53,9 +53,7 @@ template <size_t Channel>
void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t index) void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t index)
{ {
auto image = tree.get_evaluation_ref<image_t>(); auto image = tree.get_evaluation_ref<image_t>();
// image->normalize();
// std::memcpy(images[index].data.data(), image->get_data().data.data(), IMAGE_SIZE_BYTES);
auto& data = image->get_data(); auto& data = image->get_data();
for (blt::size_t x = 0; x < IMAGE_DIMENSIONS; ++x) for (blt::size_t x = 0; x < IMAGE_DIMENSIONS; ++x)
{ {
@ -63,28 +61,19 @@ void fitness_func(const tree_t& tree, fitness_t& fitness, const blt::size_t inde
{ {
images[index][(x * IMAGE_DIMENSIONS + y) * 3 + Channel] = data.get(x, y); images[index][(x * IMAGE_DIMENSIONS + y) * 3 + Channel] = data.get(x, y);
auto multiplier = (1 - std::abs((static_cast<float>(x) / (static_cast<float>(IMAGE_DIMENSIONS) / 2)) - 1)) + (1 - std::abs( auto our = static_cast<double>(data.get(x, y)) / static_cast<double>(std::numeric_limits<blt::u32>::max());
(static_cast<float>(y) / (static_cast<float>(IMAGE_DIMENSIONS) / 2)) - 1));
auto our3 = static_cast<double>(data.get(x, y)) / static_cast<double>(std::numeric_limits<blt::u32>::max());
auto theirs = reference_image[Channel].get(x, y); auto theirs = reference_image[Channel].get(x, y);
const auto diff = std::pow(our3, 2.2f) - std::pow(theirs, 2.2f);
if (std::isnan(diff)) auto gamma_ours = std::pow(our, 1.0f / 2.2f);
{ auto gamma_theirs = std::pow(theirs, 1.0f / 2.2f);
if (std::isnan(our3))
BLT_DEBUG("Our is nan"); const auto diff = gamma_ours - gamma_theirs;
if (std::isnan(theirs)) fitness.raw_fitness += static_cast<float>((diff * diff));
BLT_DEBUG("Theirs is nan");
BLT_TRACE("We got {} vs {}", our3, theirs);
continue;
}
fitness.raw_fitness += static_cast<float>(diff);
} }
} }
// fitness.raw_fitness /= static_cast<float>(IMAGE_SIZE_CHANNELS); // fitness.raw_fitness /= static_cast<float>(IMAGE_SIZE_CHANNELS);
// fitness.raw_fitness = static_cast<float>(std::sqrt(fitness.raw_fitness)); fitness.raw_fitness = static_cast<float>(std::sqrt(fitness.raw_fitness));
fitness.standardized_fitness = fitness.raw_fitness; fitness.standardized_fitness = fitness.raw_fitness;
fitness.adjusted_fitness = -fitness.standardized_fitness; fitness.adjusted_fitness = -fitness.standardized_fitness;
} }
@ -93,7 +82,17 @@ template <typename T>
void setup_operations(gp_program* program) void setup_operations(gp_program* program)
{ {
static operation_t op_image_x([]() { static operation_t op_image_x([]() {
constexpr auto mul = std::numeric_limits<blt::u32>::max() - static_cast<blt::u32>(IMAGE_DIMENSIONS - 1); constexpr auto mul = std::numeric_limits<blt::u32>::max() / static_cast<blt::u32>(IMAGE_DIMENSIONS - 1);
image_t ret{};
for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x)
{
for (blt::u32 y = 0; y < IMAGE_DIMENSIONS; ++y)
ret.get_data().get(x, y) = y * mul;
}
return ret;
});
static operation_t op_image_y([]() {
constexpr auto mul = std::numeric_limits<blt::u32>::max() / static_cast<blt::u32>(IMAGE_DIMENSIONS - 1);
image_t ret{}; image_t ret{};
for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x) for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x)
{ {
@ -102,14 +101,10 @@ void setup_operations(gp_program* program)
} }
return ret; return ret;
}); });
static operation_t op_image_y([]() { static auto op_image_random = operation_t([program]() {
constexpr auto mul = std::numeric_limits<blt::u32>::max() - static_cast<blt::u32>(IMAGE_DIMENSIONS - 1);
image_t ret{}; image_t ret{};
for (blt::u32 x = 0; x < IMAGE_DIMENSIONS; ++x) for (auto& v : ret.get_data().data)
{ v = program->get_random().get_u32(0, std::numeric_limits<blt::u32>::max());
for (blt::u32 y = 0; y < IMAGE_DIMENSIONS; ++y)
ret.get_data().get(x, y) = y * mul;
}
return ret; return ret;
}); });
static auto op_image_noise = operation_t([program]() { static auto op_image_noise = operation_t([program]() {
@ -136,32 +131,50 @@ void setup_operations(gp_program* program)
// return ret; // return ret;
// }, "blend_image"); // }, "blend_image");
static operation_t op_image_sin([](const image_t a) { static operation_t op_image_sin([](const image_t a) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data))) for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
ret.get_data().data[i] = blt::mem::type_cast<blt::u32>(static_cast<float>(std::sin(v))); ret.get_data().data[i] = static_cast<blt::u32>(((std::sin((v / limit) * blt::PI) + 1.0) / 2.0f) * limit);
return ret; return ret;
}, "sin_image"); }, "sin_image");
static operation_t op_image_sin_off([](const image_t a, const image_t b) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{};
for (const auto& [i, v, off] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
ret.get_data().data[i] = static_cast<blt::u32>(((std::sin((v / limit) * blt::PI * (off / (limit / 4))) + 1.0) / 2.0f) * limit);
return ret;
}, "sin_image_off");
static operation_t op_image_cos([](const image_t a) { static operation_t op_image_cos([](const image_t a) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data))) for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
ret.get_data().data[i] = blt::mem::type_cast<blt::u32>(static_cast<float>(std::cos(v))); ret.get_data().data[i] = static_cast<blt::u32>(((std::cos((v / limit) * blt::PI * 2) + 1.0) / 2.0f) * limit);
return ret; return ret;
}, "cos_image"); }, "cos_image");
static operation_t op_image_cos_off([](const image_t a, const image_t b) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{};
for (const auto& [i, v, off] : blt::in_pairs(std::as_const(a.get_data().data), std::as_const(b.get_data().data)).enumerate().flatten())
ret.get_data().data[i] = static_cast<blt::u32>(((std::cos((v / limit) * blt::PI * (off / (limit / 2))) + 1.0) / 2.0f) * limit);
return ret;
}, "cos_image_off");
static operation_t op_image_log([](const image_t a) { static operation_t op_image_log([](const image_t a) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data))) for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
{ {
if (v == 0) if (v == 0)
ret.get_data().data[i] = 0; ret.get_data().data[i] = 0;
else else
ret.get_data().data[i] = blt::mem::type_cast<blt::u32>(static_cast<float>(std::log(v))); ret.get_data().data[i] = static_cast<blt::u32>(std::log(v / limit) * limit);
} }
return ret; return ret;
}, "log_image"); }, "log_image");
static operation_t op_image_exp([](const image_t a) { static operation_t op_image_exp([](const image_t a) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data))) for (const auto& [i, v] : blt::enumerate(std::as_const(a.get_data().data)))
ret.get_data().data[i] = blt::mem::type_cast<blt::u32>(static_cast<float>(std::exp(v))); ret.get_data().data[i] = static_cast<blt::u32>(std::exp(v / limit) * limit);
return ret; return ret;
}, "exp_image"); }, "exp_image");
static operation_t op_image_or([](const image_t a, const image_t b) { static operation_t op_image_or([](const image_t a, const image_t b) {
@ -200,72 +213,138 @@ void setup_operations(gp_program* program)
ret.get_data().data[i] = av < bv ? av : bv; ret.get_data().data[i] = av < bv ? av : bv;
return ret; return ret;
}, "lt_image"); }, "lt_image");
static operation_t op_image_perlin([](const float ofx, const float ofy, const float ofz, const float lacunarity, const float gain, static operation_t op_image_grad([](const image_t a, const image_t b) {
const float octaves) { image_t out{}; // storage for the result
const auto& inA = a.get_data().data;
const auto& inB = b.get_data().data;
auto& dst = out.get_data().data;
constexpr std::size_t W = IMAGE_DIMENSIONS;
constexpr std::size_t H = IMAGE_DIMENSIONS;
if (true)
{
for (std::size_t y = 0; y < H; ++y)
{
for (std::size_t x = 0; x < W; ++x)
{
const double t = static_cast<double>(x) / static_cast<double>(W - 1);
const std::size_t idx = y * W + x;
const double a = static_cast<double>(inA[idx]);
const double b = static_cast<double>(inB[idx]);
dst[idx] = static_cast<blt::u32>((1.0 - t) * a + t * b);
}
}
} else // vertical gradient
{
for (std::size_t y = 0; y < H; ++y)
{
const double t = static_cast<double>(y) / static_cast<double>(H - 1);
for (std::size_t x = 0; x < W; ++x)
{
const std::size_t idx = y * W + x;
const double a = static_cast<double>(inA[idx]);
const double b = static_cast<double>(inB[idx]);
dst[idx] = static_cast<blt::u32>((1.0 - t) * a + t * b);
}
}
}
return out;
}, "grad_image");
static operation_t op_image_perlin([](const image_t a) {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, out] : blt::enumerate(ret.get_data().data)) for (const auto& [i, out, bv] : blt::in_pairs(ret.get_data().data, std::as_const(a.get_data().data)).enumerate().flatten())
out = blt::mem::type_cast<blt::u32>(stb_perlin_ridge_noise3(static_cast<float>(i % IMAGE_DIMENSIONS) + ofx, {
static_cast<float>(i / IMAGE_DIMENSIONS) + ofy, ofz, lacunarity + 2, constexpr auto AND = IMAGE_DIMENSIONS - 1;
gain + 0.5f, 1.0f, static_cast<int>(octaves))); const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
out = static_cast<blt::u32>(stb_perlin_noise3(static_cast<float>(x), static_cast<float>(y), static_cast<float>(bv / (limit * 0.1)), 0, 0,
0) * limit);
}
return ret; return ret;
}, "perlin_image"); }, "perlin_image");
static operation_t op_image_perlin_bounded([](float octaves) { static auto op_image_2d_perlin_eph = operation_t([program]() {
octaves = std::min(std::max(octaves, 4.0f), 8.0f); constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{}; image_t ret{};
for (const auto& [i, out] : blt::enumerate(ret.get_data().data)) const auto variety = program->get_random().get_float(1.5, 255);
out = blt::mem::type_cast<blt::u32>(stb_perlin_fbm_noise3(static_cast<float>(i % IMAGE_DIMENSIONS) + 0.23423f, const auto x_warp = program->get_random().get_i32(0, 255);
static_cast<float>(i) / IMAGE_DIMENSIONS + 0.6234f, 0.4861f, 2, 0.5, const auto y_warp = program->get_random().get_i32(0, 255);
static_cast<int>(octaves))); const auto z_warp = program->get_random().get_i32(0, 255);
return ret;
}, "perlin_image_bounded");
static operation_t op_sin([](const float a) {
return std::sin(a);
}, "sin_float");
static operation_t op_cos([](const float a) {
return std::cos(a);
}, "cos_float");
static operation_t op_exp([](const float a) {
return std::exp(a);
}, "exp_float");
static operation_t op_log([](const float a) {
if (blt::f_equal(a, 0))
return 0.0f;
if (a < 0)
return -std::log(-a);
return std::log(a);
}, "log_float");
static auto lit = operation_t([program]() {
return program->get_random().get_float(-1.0f, 1.0f);
}, "lit_float").set_ephemeral();
static operation_t op_erode([](const image_t a, float erosion_size) {
image_t ret{};
erosion_size = std::min(std::max(erosion_size, 0.0f), 21.0f);
const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, a.as_void_const()};
cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, ret.get_data().data.data()};
const cv::Mat element = cv::getStructuringElement( cv::MORPH_CROSS,
cv::Size( static_cast<int>(2*erosion_size + 1), static_cast<int>(2*erosion_size+1) ),
cv::Point( static_cast<int>(erosion_size), static_cast<int>(erosion_size) ) );
cv::erode( src, dst, element );
return ret;
}, "erode_image");
static operation_t op_dilate([](const image_t a, float dilate_size) { const auto offset_x = program->get_random().get_float(1.0 / 64.0f, 16.0f);
image_t ret{}; const auto offset_y = program->get_random().get_float(1.0 / 64.0f, 16.0f);
dilate_size = std::min(std::max(dilate_size, 0.0f), 21.0f);
const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, a.as_void_const()}; for (const auto& [i, out] : blt::enumerate(ret.get_data().data))
cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, ret.get_data().data.data()}; {
const cv::Mat element = cv::getStructuringElement( cv::MORPH_CROSS, constexpr auto AND = IMAGE_DIMENSIONS - 1;
cv::Size( static_cast<int>(2*dilate_size + 1), static_cast<int>(2*dilate_size+1) ), const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
cv::Point( static_cast<int>(dilate_size), static_cast<int>(dilate_size) ) ); const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
cv::dilate( src, dst, element ); out = static_cast<blt::u32>(stb_perlin_noise3(static_cast<float>(x) * offset_x, static_cast<float>(y) * offset_y, variety, x_warp, y_warp,
z_warp) * limit);
}
return ret; return ret;
}, "erode_image"); }, "perlin_image_eph").set_ephemeral();
static auto op_image_2d_perlin_oct = operation_t([program]() {
constexpr auto limit = static_cast<double>(std::numeric_limits<blt::u32>::max());
image_t ret{};
const auto rand = program->get_random().get_float(0, 255);
const auto octaves = program->get_random().get_i32(2, 8);
const auto gain = program->get_random().get_float(0.1f, 0.9f);
const auto lac = program->get_random().get_float(1.5f, 6.f);
const auto offset = program->get_random().get_float(1.0 / 255.0f, 16.0f);
for (const auto& [i, out] : blt::enumerate(ret.get_data().data))
{
constexpr auto AND = IMAGE_DIMENSIONS - 1;
const double y = (static_cast<float>(i) / IMAGE_DIMENSIONS) / static_cast<float>(IMAGE_DIMENSIONS);
const double x = static_cast<float>(i & AND) / static_cast<float>(IMAGE_DIMENSIONS);
out = static_cast<blt::u32>(stb_perlin_fbm_noise3(static_cast<float>(x * offset), static_cast<float>(y * offset), rand, lac, gain,
octaves) * limit);
}
return ret;
}, "perlin_image_eph_oct").set_ephemeral();
static operation_t op_passthrough([](const image_t& a) {
image_t ret{};
std::memcpy(ret.get_data().data.data(), a.get_data().data.data(), IMAGE_SIZE_BYTES);
return ret;
}, "passthrough");
// static operation_t op_erode([](const image_t a, float erosion_size) {
// image_t ret{};
// erosion_size = std::min(std::max(erosion_size, 0.0f), 21.0f);
// const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, a.as_void_const()};
// cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, ret.get_data().data.data()};
// const cv::Mat element = cv::getStructuringElement( cv::MORPH_CROSS,
// cv::Size( static_cast<int>(2*erosion_size + 1), static_cast<int>(2*erosion_size+1) ),
// cv::Point( static_cast<int>(erosion_size), static_cast<int>(erosion_size) ) );
// cv::erode( src, dst, element );
// return ret;
// }, "erode_image");
//
// static operation_t op_dilate([](const image_t a, float dilate_size) {
// image_t ret{};
// dilate_size = std::min(std::max(dilate_size, 0.0f), 21.0f);
// const cv::Mat src{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, a.as_void_const()};
// cv::Mat dst{IMAGE_DIMENSIONS, IMAGE_DIMENSIONS, CV_32F, ret.get_data().data.data()};
// const cv::Mat element = cv::getStructuringElement( cv::MORPH_CROSS,
// cv::Size( static_cast<int>(2*dilate_size + 1), static_cast<int>(2*dilate_size+1) ),
// cv::Point( static_cast<int>(dilate_size), static_cast<int>(dilate_size) ) );
// cv::dilate( src, dst, element );
// return ret;
// }, "erode_image");
operator_builder builder{}; operator_builder builder{};
builder.build(op_image_ephemeral, make_add<image_t>(), make_sub<image_t>(), make_mul<image_t>(), make_div<image_t>(), op_image_x, op_image_y, // builder.build(op_image_ephemeral, make_add<image_t>(), make_sub<image_t>(), make_mul<image_t>(), make_div<image_t>(), op_image_x, op_image_y,
op_image_sin, op_image_gt, op_image_lt, op_image_cos, op_image_log, op_image_exp, op_image_or, op_image_and, op_image_xor, // op_image_sin, op_image_gt, op_image_lt, op_image_cos, op_image_log, op_image_exp, op_image_or, op_image_and, op_image_xor, op_image_cos_off, op_image_sin_off op_image_perlinm
op_image_not, op_image_perlin_bounded, make_add<float>(), make_sub<float>(), make_mul<float>(), // op_image_2d_ifs_eph, op_image_noise, op_image_random, op_image_2d_perlin_eph, op_image_not, op_image_2d_perlin_oct);
make_prot_div<float>(), op_sin, op_cos, op_exp, op_log, lit); builder.build(op_image_grad, op_image_x, op_image_y);
program->set_operations(builder.grab()); program->set_operations(builder.grab());
} }
@ -356,7 +435,7 @@ void cleanup()
delete program; delete program;
} }
std::array<image_storage_t, 3>& get_reference_image() const std::array<image_storage_t, 3>& get_reference_image()
{ {
return reference_image; return reference_image;
} }

View File

@ -37,9 +37,9 @@ std::array<image_storage_t, 3> image_storage_t::from_file(const std::string& pat
{ {
for (blt::size_t j = 0; j < IMAGE_DIMENSIONS; ++j) for (blt::size_t j = 0; j < IMAGE_DIMENSIONS; ++j)
{ {
storage_r.get(i, j) = resized[(i * IMAGE_DIMENSIONS + j) * 4]; storage_r.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4];
storage_g.get(i, j) = resized[(i * IMAGE_DIMENSIONS + j) * 4 + 1]; storage_g.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4 + 1];
storage_b.get(i, j) = resized[(i * IMAGE_DIMENSIONS + j) * 4 + 2]; storage_b.get(i, j) = resized[(j * IMAGE_DIMENSIONS + x) * 4 + 2];
} }
} }

BIN
white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB