+
+
+
+
+
+
+
+
+
+
+
+
+
22inline std::string get_namespace()
+
+
24 std::string ret =
"GooseEYE";
+
25#ifdef GOOSEEYE_USE_XTENSOR_PYTHON
+
+
+
+
+
+
+
+
37inline std::string shape_to_string(
const T& shape)
+
+
39 std::string ret =
"[";
+
40 for (
size_t i = 0; i < shape.size(); ++i) {
+
41 ret += std::to_string(shape[i]);
+
42 if (i < shape.size() - 1) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
97 const std::vector<size_t>& shape,
+
+
+
+
101 bool periodic =
true)
+
+
+
+
+
+
+
+
+
110 for (
size_t i = 0; i < row.size(); ++i) {
+
111 for (
int di = -r(i); di <= r(i); ++di) {
+
112 for (
int dj = -r(i); dj <= r(i); ++dj) {
+
113 int dr = (int)(ceil(pow((
double)(pow(di, 2) + pow(dj, 2)), 0.5)));
+
+
115 out.periodic(row(i) + di, col(i) + dj) = 1;
+
+
+
+
+
+
+
+
+
124 for (
size_t i = 0; i < row.size(); ++i) {
+
125 for (
int di = -r(i); di <= r(i); ++di) {
+
126 for (
int dj = -r(i); dj <= r(i); ++dj) {
+
127 if (out.in_bounds(row(i) + di, col(i) + dj)) {
+
128 int dr = (int)(ceil(pow((
double)(pow(di, 2) + pow(dj, 2)), 0.5)));
+
+
130 out(row(i) + di, col(i) + dj) = 1;
+
+
+
+
+
+
+
+
+
+
+
+
+
148dummy_circles(
const std::vector<size_t>& shape,
bool periodic =
true, uint64_t seed = 0)
+
+
+
151 prrng::pcg32 rng(seed);
+
+
+
154 size_t N = (size_t)(0.05 * (
double)shape[0]);
+
155 size_t M = (size_t)(0.05 * (
double)shape[1]);
+
156 size_t R = (size_t)(pow((0.3 * (
double)(shape[0] * shape[1])) / (M_PI * (double)(N * M)), 0.5));
+
+
+
+
+
+
+
163 for (
size_t i = 0; i < N; i++) {
+
164 for (
size_t j = 0; j < M; j++) {
+
165 row[i * M + j] = (int)((
double)i * (double)shape[0] / (
double)N);
+
166 col[i * M + j] = (int)((
double)j * (double)shape[1] / (
double)M);
+
167 r[i * M + j] = (int)R;
+
+
+
+
+
172 int dN = (int)(0.5 * (
double)shape[0] / (double)N);
+
173 int dM = (int)(0.5 * (
double)shape[1] / (double)M);
+
+
+
176 for (
size_t i = 0; i < N * M; i++) {
+
177 row(i) += rng.randint(2 * dN) - dN;
+
178 col(i) += rng.randint(2 * dM) - dM;
+
179 r(i) = (double)r(i) * 2.0 * rng.random();
+
+
+
+
+
+
+
+
+
+
+
+
199 std::is_integral<typename T::value_type>::value &&
+
200 std::is_integral<typename S::value_type>::value,
+
+
+
+
+
+
206 bool periodic =
true);
+
+
212template <class T, std::enable_if_t<std::is_integral<typename T::value_type>::value,
int> = 0>
+
+
+
+
+
+
+
223 std::is_integral<typename T::value_type>::value &&
+
224 std::is_integral<typename S::value_type>::value,
+
+
226inline T
dilate(
const T& f,
const S& kernel,
size_t iterations = 1,
bool periodic =
true);
+
+
232template <class T, std::enable_if_t<std::is_integral<typename T::value_type>::value,
int> = 0>
+
233inline T
dilate(
const T& f,
size_t iterations = 1,
bool periodic =
true);
+
+
241template <
class T,
class S>
+
+
+
+
+
+
+
+
+
249 for (
size_t i = 0; i < a.size(); ++i) {
+
250 ret(a.flat(i)) = b.flat(i);
+
+
+
+
+
+
+
+
+
+
+
265 using value_type =
typename T::value_type;
+
266 std::map<value_type, value_type> map;
+
+
268 for (
size_t i = 0; i < a.size(); ++i) {
+
269 map.try_emplace(a.flat(i), b.flat(i));
+
+
+
+
+
274 xt::empty<typename T::value_type>(std::array<size_t, 2>{map.size(), 2});
+
+
276 for (
auto const& [key, val] : map) {
+
+
+
+
+
+
+
+
+
+
291template <
class L,
class A>
+
+
+
+
+
+
296 using value_type =
typename A::value_type;
+
297 std::map<value_type, value_type> map;
+
298 for (
size_t i = 0; i < rename.shape(0); ++i) {
+
299 map.emplace(rename(i, 0), rename(i, 1));
+
+
+
302#ifdef GOOSEEYE_ENABLE_ASSERT
+
303 auto l = xt::unique(labels);
+
304 for (
size_t i = 0; i < l.size(); ++i) {
+
+
+
+
+
309 L ret = xt::empty_like(labels);
+
310 for (
size_t i = 0; i < labels.size(); ++i) {
+
311 ret.flat(i) = map[labels.flat(i)];
+
+
+
+
+
+
+
+
+
+
+
327 using value_type =
typename T::value_type;
+
328 auto unq = xt::unique(labels);
+
329 bool background = xt::any(xt::equal(unq, 0));
+
+
331 std::array<size_t, 2> shape = {unq.size(), 2};
+
+
+
+
+
+
+
338 for (
size_t i = 1; i < unq.size(); ++i) {
+
339 rename(i, 0) = unq(i);
+
+
+
+
+
+
345 for (
size_t i = 0; i < unq.size(); ++i) {
+
+
+
+
349 rename(row, 0) = unq(i);
+
350 rename(row, 1) = row;
+
+
+
+
+
+
356 for (
size_t i = 0; i < unq.size(); ++i) {
+
357 rename(i, 0) = unq(i);
+
358 rename(i, 1) = i + 1;
+
+
+
+
+
+
+
370template <
class L,
class A>
+
+
+
+
373#ifdef GOOSEEYE_ENABLE_ASSERT
+
374 auto a = xt::unique(labels);
+
375 auto b = xt::unique(order);
+
+
+
+
+
380 auto maxlab = *std::max_element(order.begin(), order.end());
+
381 std::vector<typename A::value_type> renum(maxlab + 1);
+
+
383 for (
size_t i = 0; i < order.size(); ++i) {
+
+
+
+
387 L ret = xt::empty_like(labels);
+
388 for (
size_t i = 0; i < labels.size(); ++i) {
+
389 ret.flat(i) = renum[labels.flat(i)];
+
+
+
+
+
+
+
+
+
+
+
403 using value_type =
typename T::value_type;
+
404 std::map<value_type, value_type> map;
+
+
406 for (
size_t i = 0; i < labels.size(); ++i) {
+
407 if (map.count(labels.flat(i)) == 0) {
+
408 map.emplace(labels.flat(i), 1);
+
+
+
411 map[labels.flat(i)]++;
+
+
+
+
+
+
417 xt::empty<value_type>(std::array<size_t, 2>{map.size(), 2});
+
+
419 for (
auto const& [key, val] : map) {
+
+
+
+
+
+
+
+
+
+
+
+
435template <
size_t Dim,
class T>
+
+
+
438#ifdef GOOSEEYE_ENABLE_ASSERT
+
439 for (
size_t i = 0; i < Dim; ++i) {
+
+
+
+
+
444 std::array<size_t, Dim> mid;
+
445 for (
size_t i = 0; i < Dim; ++i) {
+
446 mid[i] = (kernel.shape(i) - 1) / 2;
+
+
+
449 for (
size_t i = 0; i < Dim; ++i) {
+
450 idx += mid[i] * kernel.strides()[i];
+
+
+
453 kernel.flat(idx) = 0;
+
+
455 if constexpr (Dim == 1) {
+
456 auto i = xt::flatten_indices(xt::argwhere(kernel)) - mid[0];
+
+
458 std::copy(i.begin(), i.end(), ret.begin());
+
+
+
+
462 auto ret = xt::from_indices(xt::argwhere(kernel));
+
463 for (
size_t i = 0; i < Dim; ++i) {
+
464 xt::view(ret, xt::all(), i) -= mid[i];
+
+
+
+
+
+
+
476template <
size_t Dimension,
bool Periodicity = true>
+
+
+
+
479 static constexpr size_t Dim = Dimension;
+
+
+
+
483 std::array<ptrdiff_t, Dim> m_shape;
+
+
+
486 ptrdiff_t m_new_label = 1;
+
+
488 std::array<ptrdiff_t, Dim> m_strides;
+
+
496 std::vector<ptrdiff_t> m_renum;
+
+
508 std::vector<ptrdiff_t> m_next;
+
509 std::vector<ptrdiff_t> m_connected;
+
+
+
+
+
+
+
+
+
+
521 if constexpr (
Dim == 1) {
+
+
+
+
525 else if constexpr (
Dim == 2) {
+
+
527 m_dx = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
+
+
529 else if constexpr (
Dim == 3) {
+
530 m_dx = {{-1, 0, 0}, {0, -1, 0}, {0, 0, -1}, {0, 0, 1}, {0, 1, 0}, {1, 0, 0}};
+
+
+
533 throw std::runtime_error(
"Please specify the kernel in dimensions > 3.");
+
+
+
+
+
+
542 template <
class T,
class K>
+
+
+
+
545 m_dx = detail::kernel_to_dx<Dim>(kernel);
+
+
+
+
+
+
+
551 void init(
const T&
shape)
+
+
553 m_label = xt::empty<ptrdiff_t>(
shape);
+
554 m_renum.resize(m_label.size() + 1);
+
555 m_next.resize(m_label.size() + 1);
+
556 for (
size_t i = 0; i <
Dim; ++i) {
+
557 m_shape[i] =
static_cast<ptrdiff_t
>(
shape[i]);
+
558 if constexpr (
Dim >= 2) {
+
559 m_strides[i] =
static_cast<ptrdiff_t
>(m_label.strides()[i]);
+
+
+
+
563 m_connected.resize(m_dx.shape(0));
+
+
+
+
567 if constexpr (
Dim == 2) {
+
568 if (m_shape[0] == 1) {
+
569 get_compare = &ClusterLabeller<Dimension, Periodicity>::get_compare_2d_1n;
+
+
571 else if (m_shape[1] == 1) {
+
572 get_compare = &ClusterLabeller<Dimension, Periodicity>::get_compare_2d_n1;
+
+
+
+
+
+
+
+
+
583 std::fill(m_label.begin(), m_label.end(), 0);
+
584 std::iota(m_renum.begin(), m_renum.end(), 0);
+
+
+
+
+
+
+
+
+
595 ptrdiff_t n =
static_cast<ptrdiff_t
>(m_new_label);
+
+
+
598 for (ptrdiff_t i = 1; i < n; ++i) {
+
599 if (m_renum[i] == i) {
+
600 m_renum[i] = m_new_label;
+
+
+
+
604 this->private_renumber(m_renum);
+
605 std::iota(m_renum.begin(), m_renum.begin() + n, 0);
+
+
+
+
+
+
+
614 std::fill(m_next.begin(), m_next.end(), -1);
+
+
+
+
622 void private_renumber(
const T& renum)
+
+
624 for (
size_t i = 0; i < m_label.size(); ++i) {
+
625 m_label.flat(i) = renum[m_label.flat(i)];
+
+
+
+
637 void merge_detail(ptrdiff_t a, ptrdiff_t b)
+
+
+
640 ptrdiff_t i = m_renum[b];
+
641 ptrdiff_t target = m_renum[a];
+
+
+
+
+
+
+
+
+
+
651 while (m_next[a] != -1) {
+
+
+
+
+
+
664 ptrdiff_t merge(ptrdiff_t*
labels,
size_t nlabels)
+
+
+
+
668 ptrdiff_t target =
labels[0];
+
669 for (
size_t i = 1; i < nlabels; ++i) {
+
670 this->merge_detail(target,
labels[i]);
+
+
+
+
+
+
+
+
+
+
+
681 this->private_renumber(m_renum);
+
+
+
+
+
690 ptrdiff_t get_compare_2d_1n(
size_t idx,
size_t j)
+
+
+
693 return (m_shape[1] + idx + m_dx(j, 1)) % m_shape[1];
+
+
+
696 ptrdiff_t compare = idx + m_dx(j, 1);
+
697 if (compare < 0 || compare >= m_shape[1]) {
+
+
+
+
+
+
+
708 ptrdiff_t get_compare_2d_n1(
size_t idx,
size_t j)
+
+
+
711 return (m_shape[0] + idx + m_dx(j, 0)) % m_shape[0];
+
+
+
714 ptrdiff_t compare = idx + m_dx(j, 0);
+
715 if (compare < 0 || compare >= m_shape[0]) {
+
+
+
+
+
+
+
730 ptrdiff_t get_compare_default(
size_t idx,
size_t j)
+
+
+
733 return (m_shape[0] + idx + m_dx.flat(j)) % m_shape[0];
+
+
+
736 ptrdiff_t compare = idx + m_dx.flat(j);
+
737 if (compare < 0 || compare >= m_shape[0]) {
+
+
+
740 return idx + m_dx.flat(j);
+
+
+
743 ptrdiff_t ii = (m_shape[0] + (idx / m_strides[0]) + m_dx(j, 0)) % m_shape[0];
+
744 ptrdiff_t jj = (m_shape[1] + (idx % m_strides[0]) + m_dx(j, 1)) % m_shape[1];
+
745 return ii * m_shape[1] + jj;
+
+
+
748 ptrdiff_t ii = (idx / m_strides[0]) + m_dx(j, 0);
+
749 ptrdiff_t jj = (idx % m_strides[0]) + m_dx(j, 1);
+
750 if (ii < 0 || ii >= m_shape[0] || jj < 0 || jj >= m_shape[1]) {
+
+
+
753 return ii * m_shape[1] + jj;
+
+
+
756 auto index = xt::unravel_from_strides(idx, m_strides, xt::layout_type::row_major);
+
757 for (
size_t d = 0; d <
Dim; ++d) {
+
758 index[d] += m_dx(j, d);
+
+
760 if (index[d] < 0 || index[d] >= m_shape[d]) {
+
+
+
+
+
+
766 index[d] = (n + (index[d] % n)) % n;
+
+
+
769 return xt::ravel_index(index, m_shape, xt::layout_type::row_major);
+
+
+
+
773 void label_impl(
size_t idx)
+
+
775 size_t nconnected = 0;
+
+
777 for (
size_t j = 0; j < m_dx.shape(0); ++j) {
+
778 ptrdiff_t compare = (this->*get_compare)(idx, j);
+
+
+
+
+
+
784 if (m_label.flat(compare) != 0) {
+
785 m_connected[nconnected] = m_renum[m_label.flat(compare)];
+
+
+
+
+
790 if (nconnected == 0) {
+
791 m_label.flat(idx) = m_new_label;
+
+
+
+
+
796 if (nconnected == 1) {
+
797 m_label.flat(idx) = m_connected[0];
+
+
+
+
+
+
+
804 m_label.flat(idx) = this->merge(&m_connected[0], nconnected);
+
+
+
+
+
+
+
+
+
816 GOOSEEYE_ASSERT(xt::has_shape(img, m_label.shape()), std::out_of_range);
+
+
818 for (
size_t idx = 0; idx < img.size(); ++idx) {
+
819 if (img.flat(idx) == 0) {
+
+
+
822 if (m_label.flat(idx) != 0) {
+
+
+
825 this->label_impl(idx);
+
+
+
+
+
+
+
+
+
+
838#ifdef GOOSEEYE_ENABLE_ASSERT
+
839 size_t n = m_label.size();
+
840 if constexpr (std::is_signed_v<typename T::value_type>) {
+
+
842 !std::any_of(begin, end, [n](
size_t i) {
return i < 0 || i >= n; }),
+
+
+
+
+
847 !std::any_of(begin, end, [n](
size_t i) {
return i >= n; }), std::out_of_range);
+
+
+
+
851 for (
auto it = begin; it != end; ++it) {
+
852 if (m_label.flat(*it) != 0) {
+
+
+
855 this->label_impl(*it);
+
+
+
+
+
+
+
+
+
+
+
868 return this->
add_points(idx.begin(), idx.end());
+
+
+
+
+
+
+
877 return detail::get_namespace() +
"ClusterLabeller" + std::to_string(
Dim) +
" " +
+
878 detail::shape_to_string(m_shape);
+
+
+
+
+
+
+
887 return m_label.shape();
+
+
+
+
+
+
+
896 return m_label.size();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
926 :
Clusters(f, kernel::nearest(f.dimension()), periodic)
+
+
+
+
+
937 template <
class T,
class S>
+
+
938 Clusters(
const T& f,
const S& kernel,
bool periodic =
true) : m_periodic(periodic)
+
+
+
941 "(please open a PR for missing functions)");
+
+
943 static_assert(std::is_integral<typename T::value_type>::value,
"Integral labels required.");
+
944 static_assert(std::is_integral<typename S::value_type>::value,
"Integral kernel required.");
+
+
946 GOOSEEYE_ASSERT(xt::all(xt::equal(f, 0) || xt::equal(f, 1)), std::out_of_range);
+
947 GOOSEEYE_ASSERT(xt::all(xt::equal(kernel, 0) || xt::equal(kernel, 1)), std::out_of_range);
+
948 GOOSEEYE_ASSERT(f.dimension() == kernel.dimension(), std::out_of_range);
+
+
950 m_shape = detail::shape(f);
+
951 m_kernel = xt::atleast_3d(kernel);
+
952 m_pad = detail::pad_width(m_kernel);
+
953 m_l = xt::atleast_3d(f);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
968 xt::view(renum, xt::keep(labels)) = xt::arange<int>(
static_cast<int>(labels.size()));
+
969 for (
auto& i : m_l) {
+
+
+
+
+
+
+
+
+
980 return xt::adapt(m_l.data(), m_shape);
+
+
+
+
+
+
+
+
+
+
992 for (
int l = 1; l < static_cast<int>(x.shape(0)); ++l) {
+
993 c(x(l, 0), x(l, 1), x(l, 2)) = l;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1009 x = this->average_position_periodic();
+
+
+
1012 x = this->average_position(m_l);
+
+
+
+
+
+
+
+
1020 return xt::view(x, xt::all(), xt::keep(axes));
+
+
+
+
+
+
+
+
+
+
1032 for (
size_t h = 0; h < m_l.shape(0); ++h) {
+
1033 for (
size_t i = 0; i < m_l.shape(1); ++i) {
+
1034 for (
size_t j = 0; j < m_l.shape(2); ++j) {
+
1035 ret(m_l(h, i, j))++;
+
+
+
+
+
+
+
+
+
+
+
+
1046 xt::pad_mode pad_mode = xt::pad_mode::constant;
+
+
+
+
1050 pad_mode = xt::pad_mode::periodic;
+
+
+
1053 m_l = xt::pad(m_l, m_pad, pad_mode, pad_value);
+
+
+
+
+
+
+
+
+
1062 array_type::tensor<int, 1> renum = xt::arange<int>(
static_cast<int>(m_l.size()));
+
+
1064 for (
size_t h = m_pad[0][0]; h < m_l.shape(0) - m_pad[0][1]; ++h) {
+
1065 for (
size_t i = m_pad[1][0]; i < m_l.shape(1) - m_pad[1][1]; ++i) {
+
1066 for (
size_t j = m_pad[2][0]; j < m_l.shape(2) - m_pad[2][1]; ++j) {
+
+
1068 if (!m_l(h, i, j)) {
+
+
+
+
+
+
1074 xt::range(h - m_pad[0][0], h + m_pad[0][1] + 1),
+
1075 xt::range(i - m_pad[1][0], i + m_pad[1][1] + 1),
+
1076 xt::range(j - m_pad[2][0], j + m_pad[2][1] + 1));
+
+
1078 auto Ni = Li * m_kernel;
+
+
1080 int l = xt::amax(Ni)();
+
+
+
+
+
+
+
1087 Li = xt::where(xt::equal(Ni, 1), l, Li);
+
+
1089 if (xt::all(xt::equal(Li, l) || xt::equal(Li, 0) || xt::equal(Li, 1))) {
+
+
+
+
+
1094 array_type::array<int> merge = xt::where(xt::less_equal(Li, 1), l, Li);
+
1095 merge = xt::unique(merge);
+
+
1097 int linkto = xt::amin(xt::view(renum, xt::keep(merge)))[0];
+
1098 for (
auto& a : merge) {
+
1099 renum = xt::where(xt::equal(renum, renum(a)), linkto, renum);
+
+
+
+
+
+
+
+
+
1108 xt::range(m_pad[0][0], m_l.shape(0) - m_pad[0][1]),
+
1109 xt::range(m_pad[1][0], m_l.shape(1) - m_pad[1][1]),
+
1110 xt::range(m_pad[2][0], m_l.shape(2) - m_pad[2][1]));
+
+
+
1113 for (
auto& i : m_l) {
+
+
+
+
+
+
1119 array_type::tensor<double, 2> average_position(
const T& lab)
const
+
+
+
1122 size_t N = xt::amax(lab)() + 1;
+
+
+
1125 array_type::tensor<double, 2> x = xt::zeros<double>({N, size_t(3)});
+
1126 array_type::tensor<double, 1> n = xt::zeros<double>({N});
+
+
1128 for (
size_t h = 0; h < lab.shape(0); ++h) {
+
1129 for (
size_t i = 0; i < lab.shape(1); ++i) {
+
1130 for (
size_t j = 0; j < lab.shape(2); ++j) {
+
+
1132 int l = lab(h, i, j);
+
+
+
1135 x(l, 0) += (double)h;
+
1136 x(l, 1) += (double)i;
+
1137 x(l, 2) += (double)j;
+
+
+
+
+
+
+
+
1145 n = xt::where(xt::equal(n, 0), 1, n);
+
+
+
1148 for (
size_t i = 0; i < x.shape(1); ++i) {
+
1149 auto xi = xt::view(x, xt::all(), i);
+
+
+
+
+
+
+
1156 array_type::tensor<double, 2> average_position_periodic()
const
+
+
+
+
+
+
1162 auto x_np = this->average_position(m_l_np);
+
+
+
1165 auto mid = detail::half_shape(m_shape);
+
+
+
1168 array_type::tensor<double, 2> shift = xt::zeros<double>({x_np.shape(0),
size_t(3)});
+
+
+
1171 for (
size_t i = 0; i < shift.shape(0); ++i) {
+
1172 for (
size_t j = 0; j < shift.shape(1); ++j) {
+
1173 if (x_np(i, j) > mid[j]) {
+
1174 shift(i, j) = -(double)m_shape[j];
+
+
+
+
+
+
1180 size_t N = xt::amax(m_l)() + 1;
+
+
+
1183 array_type::tensor<double, 2> x = xt::zeros<double>({N, size_t(3)});
+
1184 array_type::tensor<double, 1> n = xt::zeros<double>({N});
+
+
1186 for (
size_t h = 0; h < m_l.shape(0); ++h) {
+
1187 for (
size_t i = 0; i < m_l.shape(1); ++i) {
+
1188 for (
size_t j = 0; j < m_l.shape(2); ++j) {
+
+
1190 int l = m_l_np(h, i, j);
+
+
+
1193 x(relabel(l), 0) += (double)h + shift(l, 0);
+
1194 x(relabel(l), 1) += (double)i + shift(l, 1);
+
1195 x(relabel(l), 2) += (double)j + shift(l, 2);
+
1196 n(relabel(l)) += 1.0;
+
+
+
+
+
+
+
1203 n = xt::where(xt::equal(n, 0), 1, n);
+
+
+
1206 for (
size_t i = 0; i < x.shape(1); ++i) {
+
1207 auto xi = xt::view(x, xt::all(), i);
+
+
1209 xi = xt::where(xi < 0, xi + m_shape[i], xi);
+
+
+
+
+
+
1215 static const size_t MAX_DIM = 3;
+
1216 std::vector<size_t> m_shape;
+
1217 std::vector<std::vector<size_t>> m_pad;
+
1218 array_type::tensor<int, 3> m_kernel;
+
+
1220 array_type::tensor<int, 3> m_l;
+
1221 array_type::tensor<int, 3> m_l_np;
+
+
+
+
+
+
1226template <
size_t Dimension,
bool Periodicity>
+
1227class ClusterLabellerOverload :
public ClusterLabeller<Dimension, Periodicity> {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1253 GOOSEEYE_ASSERT(f.layout() == xt::layout_type::row_major, std::runtime_error);
+
+
1255 auto n = f.dimension();
+
1256 if (n == 1 && periodic) {
+
1257 return detail::ClusterLabellerOverload<1, true>(f).get();
+
+
1259 if (n == 1 && !periodic) {
+
1260 return detail::ClusterLabellerOverload<1, false>(f).get();
+
+
1262 if (n == 2 && periodic) {
+
1263 return detail::ClusterLabellerOverload<2, true>(f).get();
+
+
1265 if (n == 2 && !periodic) {
+
1266 return detail::ClusterLabellerOverload<2, false>(f).get();
+
+
1268 if (n == 3 && periodic) {
+
1269 return detail::ClusterLabellerOverload<3, true>(f).get();
+
+
1271 if (n == 3 && !periodic) {
+
1272 return detail::ClusterLabellerOverload<3, false>(f).get();
+
+
1274 throw std::runtime_error(
"Please use ClusterLabeller directly for dimensions > 3.");
+
+
+
+
1284template <
typename T,
typename U,
typename V>
+
+
1285inline T
pos2img(
const T& img,
const U& positions,
const V& labels)
+
+
+
+
1289 GOOSEEYE_ASSERT(img.dimension() == positions.shape(1), std::out_of_range);
+
1290 GOOSEEYE_ASSERT(positions.shape(0) == labels.size(), std::out_of_range);
+
+
+
1293 using value_type =
typename T::value_type;
+
+
+
1296 if (res.dimension() == 1) {
+
1297 xt::view(res, xt::keep(positions)) = labels;
+
+
1299 else if (res.dimension() == 2) {
+
1300 for (
size_t i = 0; i < positions.shape(0); ++i) {
+
1301 res(positions(i, 0), positions(i, 1)) =
static_cast<value_type
>(labels(i));
+
+
+
+
1305 for (
size_t i = 0; i < positions.shape(0); ++i) {
+
1306 res(positions(i, 0), positions(i, 1), positions(i, 2)) =
+
1307 static_cast<value_type
>(labels(i));
+
+
+
+
+
+
+
+
+
+
+
+
1328 static_assert(std::is_integral<typename T::value_type>::value,
"Integral labels required.");
+
+
+
+
+
1333 double pi = xt::numeric_constants<double>::PI;
+
1334 size_t N =
static_cast<size_t>(xt::amax(labels)(0)) + 1ul;
+
1335 size_t rank = labels.dimension();
+
1336 auto axes = detail::atleast_3d_axes(rank);
+
+
+
+
1340 for (
size_t l = 0; l < N; ++l) {
+
+
1342 xt::from_indices(xt::argwhere(xt::equal(labels, l)));
+
1343 if (positions.size() == 0) {
+
+
+
+
1347 xt::view(ret, l, xt::all()) = xt::mean(positions, 0);
+
+
+
1350 if (xt::all(xt::equal(positions, 0.0))) {
+
+
+
1353 auto theta = 2.0 * pi * positions / shape;
+
1354 auto xi = xt::cos(theta);
+
1355 auto zeta = xt::sin(theta);
+
1356 auto xi_bar = xt::mean(xi, 0);
+
1357 auto zeta_bar = xt::mean(zeta, 0);
+
1358 auto theta_bar = xt::atan2(-zeta_bar, -xi_bar) + pi;
+
1359 auto positions_bar = shape * theta_bar / (2.0 * pi);
+
1360 xt::view(ret, l, xt::all()) = positions_bar;
+
+
+
+
1364 return xt::view(ret, xt::all(), xt::keep(axes));
+
+
+
+
+
+
+
+
+
1390 Ensemble(
const std::vector<size_t>& roi,
bool periodic =
true,
bool variance =
true);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1459 void mean(
const T& f);
+
+
1466 template <
class T,
class M>
+
1467 void mean(
const T& f,
const M& fmask);
+
+
+
1475 void S2(
const T& f,
const T& g);
+
+
1484 template <
class T,
class M>
+
1485 void S2(
const T& f,
const T& g,
const M& fmask,
const M& gmask);
+
+
+
1493 void C2(
const T& f,
const T& g);
+
+
1502 template <
class T,
class M>
+
1503 void C2(
const T& f,
const T& g,
const M& fmask,
const M& gmask);
+
+
+
1511 void W2(
const T& w,
const T& f);
+
+
1519 template <
class T,
class M>
+
1520 void W2(
const T& w,
const T& f,
const M& fmask);
+
+
1529 template <
class C,
class T>
+
+
+
+
1541 template <
class C,
class T,
class M>
+
+
+
+
+
+
+
+
+
+
+
1563 template <
class T,
class M>
+
+
+
+
+
+
+
+
+
+
+
1579 Type m_stat = Type::Unset;
+
+
+
1582 static const size_t MAX_DIM = 3;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1599 std::vector<size_t> m_shape_orig;
+
+
+
1602 std::vector<size_t> m_shape;
+
+
+
1605 std::vector<std::vector<size_t>> m_pad;
+
+
+
+
+
+
+
+
1617inline auto distance(
const std::vector<size_t>& roi);
+
+
1625inline auto distance(
const std::vector<size_t>& roi,
size_t axis);
+
+
1633inline auto distance(
const std::vector<size_t>& roi,
const std::vector<double>& h);
+
+
1642inline auto distance(
const std::vector<size_t>& roi,
const std::vector<double>& h,
size_t axis);
+
+
+
1652inline auto S2(
const std::vector<size_t>& roi,
const T& f,
const T& g,
bool periodic =
true);
+
+
1663template <
class T,
class M>
+
+
1665S2(
const std::vector<size_t>& roi,
+
+
+
+
+
1670 bool periodic =
true);
+
+
+
1680inline auto C2(
const std::vector<size_t>& roi,
const T& f,
const T& g,
bool periodic =
true);
+
+
1691template <
class T,
class M>
+
+
1693C2(
const std::vector<size_t>& roi,
+
+
+
+
+
1698 bool periodic =
true);
+
+
+
1708inline auto W2(
const std::vector<size_t>& roi,
const T& w,
const T& f,
bool periodic =
true);
+
+
1718template <
class T,
class M>
+
+
1720W2(
const std::vector<size_t>& roi,
const T& w,
const T& f,
const M& fmask,
bool periodic =
true);
+
+
1731template <
class C,
class T>
+
+
1733W2c(
const std::vector<size_t>& roi,
+
+
+
+
+
1738 bool periodic =
true);
+
+
1750template <
class C,
class T,
class M>
+
+
1752W2c(
const std::vector<size_t>& roi,
+
+
+
+
+
+
1758 bool periodic =
true);
+
+
+
1767inline auto heightheight(
const std::vector<size_t>& roi,
const T& f,
bool periodic =
true);
+
+
1776template <
class T,
class M>
+
+
1778heightheight(
const std::vector<size_t>& roi,
const T& f,
const M& fmask,
bool periodic =
true);
+
+
+
+
1789L(
const std::vector<size_t>& roi,
+
+
1791 bool periodic =
true,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(Incrementally) Label clusters (0 as background, 1..n as labels).
+
void prune()
Prune: renumber labels to lowest possible label, see also AvalancheSegmenter::nlabels.
+
void add_image(const T &img)
Add image.
+
auto size() const
Size of ClusterLabeller::s and ClusterLabeller::labels (== prod(shape)).
+
ClusterLabeller(const T &shape)
+
ClusterLabeller(const T &shape, const K &kernel)
+
std::string repr() const
Basic class info.
+
const auto & shape() const
Shape of ClusterLabeller::s and ClusterLabeller::labels.
+
static constexpr bool Periodic
Periodicity of the system.
+
void add_points(const T &begin, const T &end)
Add sequence of points.
+
const auto & labels() const
Per block, the label (0 for background).
+
void add_points(const T &idx)
Add sequence of points.
+
void reset()
Reset labels to zero.
+
static constexpr size_t Dim
Dimensionality of the system.
+
Compute clusters and obtain certain characteristic about them.
+
array_type::array< int > centers() const
Return label only in the center of gravity (1..n).
+
array_type::tensor< size_t, 1 > sizes() const
Return size per cluster.
+
Clusters(const T &f, const S &kernel, bool periodic=true)
Constructor.
+
Clusters(const T &f, bool periodic=true)
Constructor.
+
array_type::array< int > labels() const
Return labels (1..n).
+
array_type::tensor< double, 2 > center_positions(bool as3d=false) const
Return positions of the centers of gravity (in the original rank, or as 3-d).
+
Compute ensemble averaged statistics, by repetitively calling the member-function of a certain statis...
+
void mean(const T &f)
Add realization to arithmetic mean.
+
void L(const T &f, path_mode mode=path_mode::Bresenham)
Add realization to lineal-path function.
+
void S2(const T &f, const T &g)
Add realization to 2-point correlation: P(f(i) * g(i + di)).
+
array_type::array< double > data_second() const
Get raw-data: ensemble sum of the second moment: x_1^2 + x_2^2 + ...
+
Ensemble()=default
Constructor.
+
array_type::array< double > variance() const
Get ensemble variance.
+
void heightheight(const T &f)
Add realization to height-height correlation.
+
void W2c(const C &clusters, const C ¢ers, const T &f, path_mode mode=path_mode::Bresenham)
Add realization to collapsed weighted 2-point correlation.
+
array_type::array< double > data_first() const
Get raw-data: ensemble sum of the first moment: x_1 + x_2 + ...
+
void W2(const T &w, const T &f)
Add realization to weighted 2-point correlation.
+
array_type::array< double > distance() const
Get the relative distance of each pixel in the 'region-of-interest' to its center.
+
array_type::array< double > result() const
Get ensemble average.
+
array_type::array< double > norm() const
Get raw-data: normalisation (number of measurements per pixel).
+
void C2(const T &f, const T &g)
Add realization to 2-point cluster function: P(f(i) == g(i + di)).
+
+
#define GOOSEEYE_ASSERT(expr, assertion)
All assertions are implemented as:
+
#define GOOSEEYE_WARNING_PYTHON(message)
Warnings specific to the Python API are implemented as:
+
+
+
+
xt::xtensor< T, N > tensor
Fixed (static) rank array.
+
array_type::array< int > nearest(size_t ndim)
Return kernel with nearest neighbours.
+
Toolbox to compute statistics.
+
path_mode
Different methods to compute a pixel-path.
+
@ Bresenham
Bresenham algorithm.
+
+
@ full
Similar to actual selecting every voxel that is crossed.
+
auto L(const std::vector< size_t > &roi, const T &f, bool periodic=true, path_mode mode=path_mode::Bresenham)
Lineal-path function.
+
T dilate(const T &f, const S &kernel, const array_type::tensor< size_t, 1 > &iterations, bool periodic)
Dilate image.
+
T labels_prune(const T &labels)
Prune labels: renumber labels to lowest possible label starting from 1.
+
array_type::tensor< double, 2 > center_of_mass(const T &labels, bool periodic=true)
Get the position of the center of each label.
+
auto W2c(const std::vector< size_t > &roi, const C &clusters, const C ¢ers, const T &f, path_mode mode=path_mode::Bresenham, bool periodic=true)
Collapsed weighted 2-point correlation.
+
array_type::tensor< typename T::value_type, 2 > labels_sizes(const T &labels)
Size per label.
+
array_type::tensor< typename T::value_type, 2 > labels_map(const T &a, const T &b)
Get a map to relabel from a to b.
+
auto C2(const std::vector< size_t > &roi, const T &f, const T &g, bool periodic=true)
2-point cluster function: P(f(i) == g(i + di)).
+
array_type::array< int > clusters(const T &f, bool periodic=true)
Compute clusters.
+
L labels_rename(const L &labels, const A &rename)
Rename labels.
+
T pos2img(const T &img, const U &positions, const V &labels)
Convert positions to an image.
+
auto W2(const std::vector< size_t > &roi, const T &w, const T &f, bool periodic=true)
Weighted 2-point correlation.
+
array_type::array< int > dummy_circles(const std::vector< size_t > &shape, const array_type::tensor< int, 1 > &row, const array_type::tensor< int, 1 > &col, const array_type::tensor< int, 1 > &r, bool periodic=true)
Dummy image with circles.
+
array_type::tensor< int, 2 > path(const array_type::tensor< int, 1 > &x0, const array_type::tensor< int, 1 > &x1, path_mode mode=path_mode::Bresenham)
Compute a path between two pixels.
+
auto distance(const std::vector< size_t > &roi)
Get the relative distance of each pixel in the 'region-of-interest' to its center.
+
array_type::tensor< size_t, 1 > relabel_map(const T &a, const S &b)
Find map to relabel from a to b.
+
auto S2(const std::vector< size_t > &roi, const T &f, const T &g, bool periodic=true)
2-point correlation: P(f(i) * g(i + di)).
+
auto heightheight(const std::vector< size_t > &roi, const T &f, bool periodic=true)
Height-height correlation.
+
L labels_reorder(const L &labels, const A &order)
Reorder labels.
+
+