void bayer(tkernel<T> & kernel) { static const auto bayer2 = std::array<size_t, 4>({ 1, 3, 4, 2 }); static const auto bayer3 = std::array<size_t, 9>({ 3, 7, 4, 6, 1, 9, 2, 8, 5 }); static const auto bayer4 = std::array<size_t, 16>({ 1, 9, 3, 11, 13, 5, 15, 7, 4, 12, 2, 10, 16, 8, 14, 6 }); static const auto bayer8 = std::array<size_t, 64>({ 1, 49, 13, 61, 4, 52, 16, 64, 33, 17, 45, 29, 36, 20, 48, 32, 9, 57, 5, 53, 12, 60, 8, 56, 41, 25, 37, 21, 44, 28, 40, 24, 3, 51, 15, 63, 2, 50, 14, 62, 35, 19, 47, 31, 34, 18, 46, 30, 11, 59, 7, 55, 10, 58, 6, 54, 43, 27, 39, 23, 42, 26, 38, 22 }); const auto size = kernel.size(); if (size != 4 && size != 9 && size != 16 && size != 64) return; // a copy of the given kernel is used to read and reassign values from const auto read_kernel = kernel; switch (kernel.size()) { case 4: for (size_t i = 0; i < 4; ++i) kernel[i] = read_kernel[bayer2[i] - 1]; break; case 9: for (size_t i = 0; i < 9; ++i) kernel[i] = read_kernel[bayer3[i] - 1]; break; case 16: for (size_t i = 0; i < 16; ++i) kernel[i] = read_kernel[bayer4[i] - 1]; break; case 64: for (size_t i = 0; i < 64; ++i) kernel[i] = read_kernel[bayer8[i] - 1]; break; } }
size_t poisson_square(tkernel<glm::tvec2<T, P>> & kernel, const unsigned int num_probes) { assert(kernel.depth() == 1); const T min_dist = 1 / sqrt(static_cast<T>(kernel.size() * sqrt(2))); return poisson_square(kernel, min_dist, num_probes); }
bool inplace_separable_filter(const tkernel& kernel, const range_t<tscalar>& range, tmatrix& src, tgetter getter, tsetter setter) { const int rows = static_cast<int>(src.rows()); const int cols = static_cast<int>(src.cols()); const int ksize = static_cast<int>(kernel.size()); const int krad = ksize / 2; if (ksize != (2 * krad + 1)) { return false; } std::vector<tscalar> buff(std::max(rows, cols)); // horizontal filter for (int r = 0; r < rows; r ++) { for (int c = 0; c < cols; c ++) { buff[c] = math::cast<tscalar>(getter(src(r, c))); } for (int c = 0; c < cols; c ++) { tscalar v = 0; for (int k = -krad; k <= krad; k ++) { const int cc = math::clamp(k + c, 0, cols - 1); v += kernel[k + krad] * buff[cc]; } src(r, c) = setter(src(r, c), math::cast<tvalue>(range.clamp(v))); } } // vertical filter for (int c = 0; c < cols; c ++) { for (int r = 0; r < rows; r ++) { buff[r] = math::cast<tscalar>(getter(src(r, c))); } for (int r = 0; r < rows; r ++) { tscalar v = 0; for (int k = -krad; k <= krad; k ++) { const int rr = math::clamp(k + r, 0, rows - 1); v += kernel[k + krad] * buff[rr]; } src(r, c) = setter(src(r, c), math::cast<tvalue>(range.clamp(v))); } } // OK return true; }
size_t poisson_square(tkernel<glm::tvec2<T, P>> & kernel, const T min_dist, const unsigned int num_probes) { assert(kernel.depth() == 1); std::random_device RD; std::mt19937_64 generator(RD()); std::uniform_real_distribution<> radius_dist(min_dist, min_dist * 2.0); std::uniform_real_distribution<> angle_dist(0.0, 2.0 * glm::pi<T>()); std::uniform_int_distribution<> int_distribute(0, std::numeric_limits<int>::max()); auto occupancy = poisson_square_map<T, P>{ min_dist }; size_t k = 0; // number of valid/final points within the kernel kernel[k] = glm::tvec2<T, P>(0.5, 0.5); auto actives = std::list<size_t>(); actives.push_back(k); occupancy.mask(kernel[k], k); while (!actives.empty() && k < kernel.size() - 1) { // randomly pick an active point const auto pick = int_distribute(generator); auto pick_it = actives.begin(); std::advance(pick_it, pick % actives.size()); const auto active = kernel[*pick_it]; std::vector<std::tuple<glm::tvec2<T, P>, T>> probes{ num_probes }; #pragma omp parallel for for (int i = 0; i < static_cast<int>(num_probes); ++i) { const auto r = radius_dist(generator); const auto a = angle_dist(generator); auto probe = glm::tvec2<T, P>{ active.x + r * cos(a), active.y + r * sin(a) }; // within square? (tilable) if (probe.x < 0.0) probe.x += 1.0; else if (probe.x >= 1.0) probe.x -= 1.0; if (probe.y < 0.0) probe.y += 1.0; else if (probe.y >= 1.0) probe.y -= 1.0; // Note: do NOT make this optimization //if (!tilable && (probe.x < 0.0 || probe.x > 1.0 || probe.y < 0.0 || probe.y > 1.0)) // continue; // points within min_dist? const auto masked = occupancy.masked(probe, kernel); const auto delta = glm::abs(active - probe); probes[i] = std::make_tuple<glm::tvec2<T, P>, T>(std::move(probe), (masked ? static_cast<T>(-1.0) : glm::dot(delta, delta))); } // pick nearest probe from sample set glm::vec2 nearest_probe; auto nearest_dist = 4 * min_dist * min_dist; auto nearest_found = false; for (int i = 0; i < static_cast<int>(num_probes); ++i) { // is this nearest point yet? - optimized by using square distance -> skipping sqrt const auto new_dist = std::get<1>(probes[i]); if (new_dist < 0.0 || nearest_dist < new_dist) continue; if (!nearest_found) nearest_found = true; nearest_dist = new_dist; nearest_probe = std::get<0>(probes[i]); } if (!nearest_found && (actives.size() > 0 || k > 1)) { actives.erase(pick_it); continue; } kernel[++k] = nearest_probe; actives.push_back(k); occupancy.mask(nearest_probe, k); } return k + 1; }
void bucket_permutate(tkernel<T> & kernel , const glm::uint16 subkernel_width , const glm::uint16 subkernel_height , const glm::uint16 subkernel_depth , const bool permutate_per_bucket) { assert(subkernel_width > 0); assert(subkernel_height > 0); assert(subkernel_depth > 0); assert(subkernel_width <= kernel.width()); assert(subkernel_height <= kernel.height()); assert(subkernel_depth <= kernel.depth()); assert(kernel.width() % subkernel_width == 0); assert(kernel.height() % subkernel_height == 0); assert(kernel.depth() % subkernel_depth == 0); // the number of the elements required to fill a sub-kernel is the number of required buckets const auto num_buckets = subkernel_width * subkernel_height * subkernel_depth; assert(kernel.size() % num_buckets == 0); if (num_buckets == 0) return; std::srand(static_cast<unsigned int>(std::time(0))); // the number of sub-kernels is also the number of values per bucket const auto num_subkernels = static_cast<int>(kernel.size() / num_buckets); // create buckets by sequentially adding every kernel index and shuffling every // bucket individually to "randomly" pop back from later on ... auto buckets = std::vector<std::vector<size_t>>{ }; buckets.resize(num_buckets); auto index = 0; for (int b = 0; b < buckets.size(); ++b) { for (int i = 0; i < num_subkernels; ++i) buckets[b].push_back(index++); std::random_shuffle(buckets[b].begin(), buckets[b].end()); } // use permutations to pop the last item of each bucket, while // selecting the bucket based on the subkernels permutation ... auto subkernel_indices = std::vector<size_t>{ }; subkernel_indices.resize(num_buckets); // a copy of the given kernel is used to read and reassign values from const auto read_kernel = kernel; const auto kw_over_w = kernel.width() / subkernel_width; const auto kh_over_h = kernel.height() / subkernel_height; const auto w_step = subkernel_width; const auto h_step = subkernel_height * kernel.width(); const auto d_step = subkernel_depth * kernel.width() * kernel.height(); // create permutations (or use single, static permutation) abstract_permutations * permutations{ nullptr }; if (permutate_per_bucket) permutations = new unique_index_permutations{ num_buckets, num_subkernels }; else permutations = new static_index_permutation{ num_buckets }; for (int k = 0; k < num_subkernels; ++k) { const auto offset = w_step * (k % kw_over_w) + h_step * ((k / kw_over_w) % kh_over_h) + d_step * (k / (kw_over_w * kh_over_h)); // retrieve indices to map subkernel indices to the given kernel auto i = 0; for (int d = 0; d < subkernel_depth; ++d) for (int h = 0; h < subkernel_height; ++h) for (int w = 0; w < subkernel_width; ++w) subkernel_indices[i++] = offset + d * kernel.width() * kernel.height() + h * kernel.width() + w; for (int i_permutation = 0; i_permutation < num_buckets; ++i_permutation) { const auto i_bucket = (*permutations)(k, i_permutation); const auto i_read = buckets[i_bucket].back(); const auto i_kernel = subkernel_indices[i_permutation]; kernel[i_kernel] = read_kernel[i_read]; buckets[i_bucket].pop_back(); } } delete permutations; permutations = nullptr; }