Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ This python3 project provides tools for computing quality indicators for multipo
The methods can also be applied to 2D or 3D images.

Currently the module provides :
- An Image class, with various import/export/conversion methods to different data types
- Functions for evaluating connectivity, histograms and variograms of 2D and 3D categorical images.
- **An `Image` class** (`mpstool.img`) for handling 2D and 3D categorical or continuous grids, with:
- Import/export to GSLIB, raw text, PNG, MagicaVoxel (`.vox`), VTK, PGM and PPM formats
- Transformations: thresholding (continuous → categorical), automatic categorization (1D k-means), normalization, axis flips/permutations, and random sub-sampling
- Visualization: 2D plots and 3D orthogonal cross-section cuts
- **Spatial statistics** (`mpstool.stats`): histograms (facies proportions) and indicator/continuous variograms, computed with a spatial-shift method
- **Connectivity analysis** (`mpstool.connectivity`): connectivity functions and maps describing how categories connect across distance, plus a threshold-based connectivity index (`gamma`) for continuous fields
- **FFT-accelerated variogram maps** (`mpstool.variogram`): full 2D variogram maps computed via FFT (Marcotte, 1996), which natively handle missing data (NaN) and are much faster than the spatial-shift method on large grids; includes a variogram-comparison metric for scoring simulation quality against a reference image
- **Cross-validation metrics** (`mpstool.cv_metrics`): probabilistic scoring rules (Brier score, CRPS, 0-1 score, linear score, and their class-balanced/skill-score variants) implementing the scikit-learn scorer interface, for cross-validating spatial simulators
- **Command-line tools** (`tools/`): `gslib-plot.py` for quickly visualizing a `.gslib` file, and `geone_cv.py` for cross-validating the `geone`/DeeSSe multi-point simulator via `GridSearchCV`, driven by a JSON configuration file

Note: `mpstool.variogram` and `mpstool.cv_metrics` are not imported automatically with `import mpstool` — import them explicitly if you need them.

## Example: connectivity function
Connectivity function describes how different categories are connected depending on distance. It is given by: ![connectivity](assets/connectivity.png)
Expand Down
2 changes: 1 addition & 1 deletion mpstool/img.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def __eq__(self, other):
for k in self._data.keys():
if self._data[k].shape != other._data[k].shape:
return False
if not np.alltrue(self._data[k] == other._data[k]):
if not np.all(self._data[k] == other._data[k]):
return False
return True

Expand Down
26 changes: 13 additions & 13 deletions tests/test_connectivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_threshold():
thresholds = np.array([1.0, 2.0])
image.threshold(thresholds)
labels = mpstool.img.labelize(image)
assert np.alltrue(labels == categorical_ref)
assert np.all(labels == categorical_ref)


def test_connected_component(cube):
Expand All @@ -69,7 +69,7 @@ def test_connected_component(cube):
[[0, 2, 2],
[0, 2, 2],
[0, 0, 0]]])
assert np.alltrue(mpstool.connectivity.get_components(
assert np.all(mpstool.connectivity.get_components(
cube, background=0) == connectivity_array_cube)


Expand All @@ -85,7 +85,7 @@ def test_get_map(array, image):
real_map = mpstool.connectivity.get_map(ar)
assert real_map.keys() == expected_map.keys()
for k in expected_map.keys():
assert np.alltrue(real_map[k] == expected_map[k])
assert np.all(real_map[k] == expected_map[k])


def test_function_2D(array, image):
Expand All @@ -97,9 +97,9 @@ def test_function_2D(array, image):
axis1_result = mpstool.connectivity.get_function(ar, axis=1)

for key in axis0_connectivity:
assert np.alltrue(axis0_result[key] == axis0_connectivity[key])
assert np.all(axis0_result[key] == axis0_connectivity[key])
for key in axis1_connectivity:
assert np.alltrue(axis1_result[key] == axis1_connectivity[key])
assert np.all(axis1_result[key] == axis1_connectivity[key])


def test_truncated_function_2D(array, image):
Expand All @@ -111,9 +111,9 @@ def test_truncated_function_2D(array, image):
axis1_result = mpstool.connectivity.get_function(ar, axis=1, max_lag=1)

for key in axis0_connectivity:
assert np.alltrue(axis0_result[key] == axis0_connectivity[key])
assert np.all(axis0_result[key] == axis0_connectivity[key])
for key in axis1_connectivity:
assert np.alltrue(axis1_result[key] == axis1_connectivity[key])
assert np.all(axis1_result[key] == axis1_connectivity[key])


def test_function_3D(extruded_array):
Expand All @@ -126,9 +126,9 @@ def test_function_3D(extruded_array):
axis2_result = mpstool.connectivity.get_function(extruded_array, axis=2)

for key in axis0_connectivity:
assert np.alltrue(axis0_result[key] == axis0_connectivity[key])
assert np.alltrue(axis1_result[key] == axis1_connectivity[key])
assert np.alltrue(axis2_result[key] == axis2_connectivity[key])
assert np.all(axis0_result[key] == axis0_connectivity[key])
assert np.all(axis1_result[key] == axis1_connectivity[key])
assert np.all(axis2_result[key] == axis2_connectivity[key])


@pytest.fixture
Expand All @@ -149,9 +149,9 @@ def test_apply_threshold(c_image):

result2 = np.ones_like(c_image)

assert np.alltrue(
assert np.all(
result1 == mpstool.connectivity._apply_threshold(c_image, 0.2))
assert np.alltrue(
assert np.all(
result2 == mpstool.connectivity._apply_threshold(c_image, 0.95))


Expand All @@ -168,5 +168,5 @@ def test_gamma(c_image):


def test_gamma_function(c_image):
assert np.alltrue(mpstool.connectivity.gamma_function(
assert np.all(mpstool.connectivity.gamma_function(
c_image, [0, 0.01, 0.92], True) == [1, 17/25, 0])
40 changes: 20 additions & 20 deletions tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ def test_saturate(img):
[0, 0, 255],
[250, 0, 0]]).reshape((3, 3, 1))
assert saturated.shape == expected.shape
assert np.alltrue(saturated == expected)
assert np.all(saturated == expected)


def test_labelize(img):
expected = np.array([[4, 6, 2],
[3, 1, 6],
[5, 3, 0]])
labels = labelize(img)
assert np.alltrue(labels == expected)
assert np.all(labels == expected)


def test_from_list():
Expand All @@ -60,7 +60,7 @@ def test_from_list():
input_data = [data1, data2]
expected = np.array(input_data)
img = Image.fromArray(input_data).asArray()
assert np.alltrue(img == expected)
assert np.all(img == expected)


# ------ Test of conversion functions ------
Expand All @@ -76,48 +76,48 @@ def test_conversion_txt_gslib():
img = Image.fromTxt("tests/data/test_img.txt", (3, 3))
img.exportAsGslib("tests/data/test_img.gslib")
img_test = Image.fromGslib("tests/data/test_img.gslib")
assert np.alltrue(img_test == img)
assert np.all(img_test == img)


def test_gslib_to_vtk():
img = Image.fromGslib("tests/data/test_img.gslib")
img.exportAsVtk("tests/data/test_img.vtk")
img2 = img.fromVtk("tests/data/test_img.vtk")
assert np.alltrue(img == img2)
assert np.all(img == img2)


def test_vtk_pgm():
img = Image.fromVtk("tests/data/test_img.vtk")
img.exportAsPgm("tests/data/test_img.pgm")
img2 = img.fromPgm("tests/data/test_img.pgm")
assert np.alltrue(img == img2)
assert np.all(img == img2)


def test_pgm_vox():
img = Image.fromPgm("tests/data/test_img.pgm")
img.exportAsVox("tests/data/test_img.vox")
img_test = Image.fromVox("tests/data/test_img.vox")
assert np.alltrue(img_test == img)
assert np.all(img_test == img)


def test_conversion_vox_png():
img = Image.fromVox("tests/data/test_img.vox")
img.exportAsPng("tests/data/test_img.png")
img_test = Image.fromPng("tests/data/test_img.png")
assert np.alltrue(img_test == img)
assert np.all(img_test == img)


def test_conversion_png_txt():
img = Image.fromPng("tests/data/test_img.png")
img.exportAsTxt("tests/data/test_img2.txt")
img_test = Image.fromTxt("tests/data/test_img2.txt", (3, 3))
assert np.alltrue(img_test == img)
assert np.all(img_test == img)


def test_conversion_final():
a = np.loadtxt("tests/data/test_img.txt").reshape((3, 3))
b = np.loadtxt("tests/data/test_img2.txt")
return np.alltrue(a == b)
return np.all(a == b)


def test_import_gslb2var():
Expand All @@ -126,8 +126,8 @@ def test_import_gslb2var():
var2 = img.asArray("V1").reshape((3, 3))
expected1 = np.array([[1, 1, 1], [1, 1, 1], [0, 0, 0]])
expected2 = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
assert np.alltrue(var1 == expected1)
assert np.alltrue(var2 == expected2)
assert np.all(var1 == expected1)
assert np.all(var2 == expected2)


def test_gslib_io():
Expand All @@ -145,7 +145,7 @@ def test_conversion_color():
img.exportAsPpm("tests/data/test_color_simple.ppm")
img2 = Image.fromPng("tests/data/test_color_simple.png")
img3 = Image.fromPpm("tests/data/test_color_simple.ppm")
assert np.alltrue((img.asArray()-img2.asArray()) < 1e8)
assert np.all((img.asArray()-img2.asArray()) < 1e8)
assert img == img3


Expand All @@ -156,7 +156,7 @@ def test_conversion_color2():
img.exportAsPpm("tests/data/test_color.ppm")
img2 = Image.fromPng("tests/data/test_color.png")
img3 = Image.fromPpm("tests/data/test_color.ppm")
assert np.alltrue((img.asArray()-img2.asArray()) < 1e8)
assert np.all((img.asArray()-img2.asArray()) < 1e8)
assert img == img3


Expand Down Expand Up @@ -192,9 +192,9 @@ def test_dimension(img):
assert img.zmin() == 0
assert img.zmax() == 1
coords = np.array([0.5, 1.5, 2.5])
assert np.alltrue(img.x() == coords)
assert np.alltrue(img.y() == coords)
assert np.alltrue(img.z() == np.array([0.5]))
assert np.all(img.x() == coords)
assert np.all(img.y() == coords)
assert np.all(img.z() == np.array([0.5]))
assert img.vmin() == 0
assert img.vmax() == 255
assert img.get_variables() == ["V0"]
Expand All @@ -206,12 +206,12 @@ def test_variable(img):
[250, 100, 0]])
img.add_variable("test", data)
assert set(img.get_variables()) == {"V0", "test"}
assert np.alltrue(img._data["test"] == data)
assert np.all(img._data["test"] == data)
data = np.zeros(img.shape)
img.set_variable("test", data)
assert np.alltrue(img._data["test"] == data)
assert np.all(img._data["test"] == data)
img.rename_variable("test", "toto")
assert np.alltrue(img._data["toto"] == data)
assert np.all(img._data["toto"] == data)
img.remove_variable("toto")
assert img.get_variables() == ["V0"]

Expand Down
Loading