Skip to content

Commit

Permalink
Overhaul cluster algorithm (3d extension needed) (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdegeus authored Nov 25, 2023
1 parent 1c59888 commit 6964f16
Show file tree
Hide file tree
Showing 25 changed files with 1,929 additions and 500 deletions.
Binary file modified docs/examples/C2.h5
Binary file not shown.
338 changes: 169 additions & 169 deletions docs/examples/C2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/examples/clusters.h5
Binary file not shown.
51 changes: 40 additions & 11 deletions docs/examples/clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
parser.add_argument("--save", action="store_true")
parser.add_argument("--check", action="store_true")
parser.add_argument("--plot", action="store_true")
parser.add_argument("--show", action="store_true")
args = parser.parse_args()

if args.save:
Expand All @@ -44,6 +45,7 @@
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.cm as cm
import prrng
from mpl_toolkits.axes_grid1 import make_axes_locatable

# color-scheme: modify such that the background is white
Expand All @@ -52,12 +54,30 @@
cmap[0, :3] = 1.0
cmap = mpl.colors.ListedColormap(cmap)

# reshuffle for better visualisation
assert np.all(np.diff(np.unique(clusters)) == 1)
assert np.all(np.diff(np.unique(clusters_periodic)) == 1)
assert np.unique(clusters).size >= np.unique(clusters_periodic).size
rng = prrng.pcg32()
lab = np.unique(clusters)
lab = lab[lab != 0]
new = np.copy(lab).astype(np.int64)
rng.shuffle(new)
rename = np.array(([0] + list(lab), [0] + list(new))).T
clusters = GooseEYE.labels_rename(clusters, rename)
lmap = GooseEYE.labels_map(clusters_periodic, clusters)
unq, unq_idx, unq_cnt = np.unique(lmap[:, 1], return_inverse=True, return_counts=True)
assert np.all(np.in1d(np.unique(clusters_periodic), lmap[:, 0]))
assert np.all(np.equal(np.sort(lmap[:, 0]), np.unique(lmap[:, 0])))
assert np.all(np.equal(np.sort(lmap[:, 1]), np.unique(lmap[:, 1])))
clusters_periodic = GooseEYE.labels_rename(clusters_periodic, lmap)

try:
plt.style.use(["goose", "goose-latex"])
except OSError:
pass

fig, axes = plt.subplots(figsize=(18, 6), nrows=1, ncols=3)
fig, axes = plt.subplots(figsize=(8 * 4, 6), nrows=1, ncols=4)

ax = axes[0]
im = ax.imshow(img, clim=(0, 1), cmap=mpl.colors.ListedColormap(cm.gray([0, 255])))
Expand All @@ -82,24 +102,33 @@
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$")
ax.set_title(r"clusters")
div = make_axes_locatable(ax)
cax = div.append_axes("right", size="5%", pad=0.1)
cbar = plt.colorbar(im, cax=cax)
cbar.set_ticks([])

ax = axes[2]
im = ax.imshow(clusters_periodic, clim=(0, np.max(clusters) + 1), cmap=cmap)
im = ax.imshow(clusters_periodic, clim=(0, np.max(clusters_periodic) + 1), cmap=cmap)
ax.xaxis.set_ticks([0, 500])
ax.yaxis.set_ticks([0, 500])
ax.set_xlim([0, 500])
ax.set_ylim([0, 500])
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$")
ax.set_title(r"clusters (periodic)")
div = make_axes_locatable(ax)
cax = div.append_axes("right", size="5%", pad=0.1)
cbar = plt.colorbar(im, cax=cax)
cbar.set_ticks([])

fig.savefig(root / "clusters.svg")
ax = axes[3]
im = ax.imshow(
np.where(clusters_periodic != clusters, clusters_periodic, 0),
clim=(0, np.max(clusters_periodic) + 1),
cmap=cmap,
)
ax.xaxis.set_ticks([0, 500])
ax.yaxis.set_ticks([0, 500])
ax.set_xlim([0, 500])
ax.set_ylim([0, 500])
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$")
ax.set_title(r"difference")

if args.show:
plt.show()
else:
fig.savefig(root / "clusters.svg")
plt.close(fig)
557 changes: 362 additions & 195 deletions docs/examples/clusters.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ channels:
dependencies:
- catch2
- cmake
- docopt
- doxygen
- enstat >=0.9.7
- h5py
Expand All @@ -16,6 +15,7 @@ dependencies:
- python
- python-prrng
- scikit-build
- scipy
- setuptools_scm
- xsimd
- xtensor
Expand Down
8 changes: 4 additions & 4 deletions include/GooseEYE/Ensemble.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace GooseEYE {
inline Ensemble::Ensemble(const std::vector<size_t>& roi, bool periodic, bool variance)
: m_periodic(periodic), m_variance(variance), m_shape_orig(roi)
{
GOOSEEYE_ASSERT(m_shape_orig.size() <= MAX_DIM);
GOOSEEYE_ASSERT(m_shape_orig.size() <= MAX_DIM, std::out_of_range);

m_first = xt::atleast_3d(xt::zeros<double>(m_shape_orig));
m_second = zeros_like(m_first);
Expand Down Expand Up @@ -70,7 +70,7 @@ inline array_type::array<double> Ensemble::norm() const

inline array_type::array<double> Ensemble::distance(size_t axis) const
{
GOOSEEYE_ASSERT(axis < m_shape_orig.size());
GOOSEEYE_ASSERT(axis < m_shape_orig.size(), std::out_of_range);
axis = detail::atleast_3d_axis(m_shape_orig.size(), axis);

array_type::tensor<double, 3> dist = xt::empty<double>(m_shape);
Expand Down Expand Up @@ -113,7 +113,7 @@ inline array_type::array<double> Ensemble::distance() const

inline array_type::array<double> Ensemble::distance(const std::vector<double>& h) const
{
GOOSEEYE_ASSERT(m_shape_orig.size() == h.size());
GOOSEEYE_ASSERT(m_shape_orig.size() == h.size(), std::out_of_range);

array_type::array<double> ret = xt::zeros<double>(m_shape_orig);

Expand All @@ -126,7 +126,7 @@ inline array_type::array<double> Ensemble::distance(const std::vector<double>& h

inline array_type::array<double> Ensemble::distance(const std::vector<double>& h, size_t axis) const
{
GOOSEEYE_ASSERT(m_shape_orig.size() == h.size());
GOOSEEYE_ASSERT(m_shape_orig.size() == h.size(), std::out_of_range);
return this->distance(axis) * h[axis];
}

Expand Down
14 changes: 7 additions & 7 deletions include/GooseEYE/Ensemble_C2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ inline void Ensemble::C2(const T& f, const T& g, const M& fmask, const M& gmask)
static_assert(std::is_integral<value_type>::value, "Integral image required.");
static_assert(std::is_integral<mask_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()));
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()));
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()));
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)));
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)));
GOOSEEYE_ASSERT(m_stat == Type::C2 || m_stat == Type::Unset);
GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::C2 || m_stat == Type::Unset, std::out_of_range);

// lock statistic
m_stat = Type::C2;
Expand Down
4 changes: 2 additions & 2 deletions include/GooseEYE/Ensemble_L.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ inline void Ensemble::L(const T& f, path_mode mode)

static_assert(std::is_integral<value_type>::value, "Integral image required.");

GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(m_stat == Type::L || m_stat == Type::Unset);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::L || m_stat == Type::Unset, std::out_of_range);

// lock statistics
m_stat = Type::L;
Expand Down
14 changes: 7 additions & 7 deletions include/GooseEYE/Ensemble_S2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ inline void Ensemble::S2(const T& f, const T& g, const M& fmask, const M& gmask)

static_assert(std::is_integral<mask_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()));
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()));
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()));
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)));
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)));
GOOSEEYE_ASSERT(m_stat == Type::S2 || m_stat == Type::Unset);
GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::S2 || m_stat == Type::Unset, std::out_of_range);

// lock statistic
m_stat = Type::S2;
Expand Down
10 changes: 5 additions & 5 deletions include/GooseEYE/Ensemble_W2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ inline void Ensemble::W2(const T& f, const T& g, const M& gmask)

static_assert(std::is_integral<mask_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()));
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()));
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)));
GOOSEEYE_ASSERT(m_stat == Type::W2 || m_stat == Type::Unset);
GOOSEEYE_ASSERT(xt::has_shape(f, g.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, gmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(gmask, 0) || xt::equal(gmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::W2 || m_stat == Type::Unset, std::out_of_range);

// lock statistic
m_stat = Type::W2;
Expand Down
12 changes: 6 additions & 6 deletions include/GooseEYE/Ensemble_W2c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ Ensemble::W2c(const C& clusters, const C& centers, const T& f, const M& fmask, p
static_assert(std::is_integral<cluster_type>::value, "Integral clusters required.");
static_assert(std::is_integral<mask_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(f.shape() == clusters.shape());
GOOSEEYE_ASSERT(f.shape() == centers.shape());
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()));
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)));
GOOSEEYE_ASSERT(m_stat == Type::W2c || m_stat == Type::Unset);
GOOSEEYE_ASSERT(f.shape() == clusters.shape(), std::out_of_range);
GOOSEEYE_ASSERT(f.shape() == centers.shape(), std::out_of_range);
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::W2c || m_stat == Type::Unset, std::out_of_range);

// lock statistic
m_stat = Type::W2c;
Expand Down
8 changes: 4 additions & 4 deletions include/GooseEYE/Ensemble_heightheight.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ inline void Ensemble::heightheight(const T& f, const M& fmask)

static_assert(std::is_integral<mask_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()));
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size());
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)));
GOOSEEYE_ASSERT(m_stat == Type::heightheight || m_stat == Type::Unset);
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(f.dimension() == m_shape_orig.size(), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::heightheight || m_stat == Type::Unset, std::out_of_range);

// lock statistic
m_stat = Type::heightheight;
Expand Down
12 changes: 6 additions & 6 deletions include/GooseEYE/Ensemble_mean.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace GooseEYE {
template <class T>
void Ensemble::mean(const T& f)
{
GOOSEEYE_ASSERT(m_shape == std::vector<size_t>(MAX_DIM, 1));
GOOSEEYE_ASSERT(m_stat == Type::mean || m_stat == Type::Unset);
GOOSEEYE_ASSERT(m_shape == std::vector<size_t>(MAX_DIM, 1), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::mean || m_stat == Type::Unset, std::out_of_range);

m_stat = Type::mean;

Expand All @@ -29,10 +29,10 @@ void Ensemble::mean(const T& f, const M& fmask)
{
static_assert(std::is_integral<typename M::value_type>::value, "Integral mask required.");

GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()));
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)));
GOOSEEYE_ASSERT(m_shape == std::vector<size_t>(MAX_DIM, 1));
GOOSEEYE_ASSERT(m_stat == Type::mean || m_stat == Type::Unset);
GOOSEEYE_ASSERT(xt::has_shape(f, fmask.shape()), std::out_of_range);
GOOSEEYE_ASSERT(xt::all(xt::equal(fmask, 0) || xt::equal(fmask, 1)), std::out_of_range);
GOOSEEYE_ASSERT(m_shape == std::vector<size_t>(MAX_DIM, 1), std::out_of_range);
GOOSEEYE_ASSERT(m_stat == Type::mean || m_stat == Type::Unset, std::out_of_range);

m_stat = Type::mean;

Expand Down
Loading

0 comments on commit 6964f16

Please sign in to comment.