Skip to content

Commit

Permalink
Add file download and histogram stats
Browse files Browse the repository at this point in the history
  • Loading branch information
BC-Chang committed Nov 26, 2024
1 parent 1c9ad8f commit 9bfe396
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 29 deletions.
25 changes: 2 additions & 23 deletions docs/tutorial/tutorial-minkowski-functionals.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"4. Euler Characteristic\n",
"\n",
"In this notebook, we compare Minkowski functional calculations via 2 methods: \n",
"- Counts arrangements of $2^3$ neighborhoods of voxels using Quantimpy [[1]](#1) \n",
"- Counts arrangements of $2^3$ neighborhoods of voxels using DPM Tools [[1]](#1) \n",
"- Creates a mesh using Scikit-Image[[2]](#2), Trimesh, and Porespy[[4]](#4) \n",
"\n",
"______\n",
Expand Down Expand Up @@ -111,28 +111,7 @@
"sphere[id_a:id_b, id_a:id_b, id_a:id_b] = ball(radius, dtype=np.uint8)\n",
"\n",
"# DPM Tools Image object\n",
"sphere = Image(scalar=sphere)\n",
"\n",
"# plotter_obj = pv.Plotter(notebook=True, off_screen=False, lighting='three lights')\n",
"\n",
"# # Set background colors\n",
"# plotter_obj.set_background(color='w')\n",
"\n",
"# # Set font colors and sizes\n",
"# pv.global_theme.font.color = 'black'\n",
"# pv.global_theme.font.size = 18\n",
"# pv.global_theme.font.label_size = 14\n",
"\n",
"# pv.set_jupyter_backend('static')\n",
"\n",
"# plotter_obj = plot_isosurface(sphere, plotter_obj, show_isosurface=[0.5], \n",
"# mesh_kwargs={\"opacity\":1, \n",
"# \"color\":(200 / 255, 181 / 255, 152 / 255), \n",
"# \"diffuse\": 0.75, \n",
"# \"ambient\": 0.15})\n",
"\n",
"\n",
"# plotter_obj.show(jupyter_backend='static')"
"sphere = Image(scalar=sphere)\n"
]
},
{
Expand Down
5 changes: 2 additions & 3 deletions dpm_tools/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
natural_sort
combine_slices
convert_filetype
download_file
"""

from ._io_utils import find_files_with_ext, get_tiff_metadata, natural_sort, combine_slices, convert_filetype
from ._io_utils import find_files_with_ext, get_tiff_metadata, natural_sort, combine_slices, convert_filetype, download_file

from ._read_data import read_image, Image

Expand Down
19 changes: 19 additions & 0 deletions dpm_tools/io/_io_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pathlib
from typing import Any, Tuple
import re
import wget

from ._read_data import read_image
from ._write_data import write_image
Expand Down Expand Up @@ -184,3 +185,21 @@ def convert_filetype(filepath: pathlib.Path, convert_to: str, **kwargs) -> None:
new_filepath = filepath.with_suffix(convert_to)
write_image(save_path=new_filepath.parent, save_name=new_filepath.name,
image=original_image, filetype=convert_to)

def download_file(url: str, save_path: str = ".") -> None:
"""
Download a file from the provided URL and save it to the specified save path.
Args:
url (str): URL of the file to download
save_path (pathlib.Path, optional): File path where to save the file to. Defaults to pathlib.Path(".").
Returns:
None
"""

# savepath = pathlib.Path(save_path)
wget.download(url, out=save_path)

return

3 changes: 2 additions & 1 deletion dpm_tools/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
_morph_drain_config
heterogeneity_curve
minkowski_map
histogram_statistics
"""

from ._maps import slicewise_edt, slicewise_mis, edt, sdt, mis, chords, time_of_flight, constriction_factor, minkowski_map

from ._feature_utils import _morph_drain_config, _set_linear_trend

from ._scalars import minkowski_functionals, morph_drain, heterogeneity_curve
from ._scalars import minkowski_functionals, morph_drain, heterogeneity_curve, histogram_statistics
73 changes: 72 additions & 1 deletion dpm_tools/metrics/_scalars.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import numpy as np
from typing import Tuple
import matplotlib.pyplot as plt
from scipy import stats
from typing import Tuple, List, Dict
import edt
from ._minkowski_coeff import contributions_2d, contributions_3d
from ._feature_utils import _morph_drain_config, _get_heterogeneity_centers_3d
Expand Down Expand Up @@ -172,3 +174,72 @@ def heterogeneity_curve(image: np.ndarray, no_radii: int = 50, n_samples_per_rad
variance[i] = np.var(porosity)

return radii, variance

def _hist_stats(image: np.ndarray, nbins: int = 256) -> Dict:
"""
Calculate histogram statistics of an image.
Parameters:
image: A 2D or 3D image.
nbins: Number of histogram bins. Defaults to 256.
Returns:
dict: A dictionary of image metadata and histogram statistics.
Includes the shape, dtype, min, max, mean, median, variance, skewness, and kurtosis.
"""

stats_dict = {'shape': image.shape,
'dtype': image.dtype,
'min': np.amin(image),
'max': np.amax(image),
'mean': np.mean(image),
'median': np.median(image),
'variance': np.var(image)}

hist, bin_edges = np.histogram(image.flatten(), bins=256)
bin_centers = 0.5 * (bin_edges[:-1] + bin_edges[1:])

stats_dict['skewness'] = stats.skew(bin_centers)
stats_dict['kurtosis'] = stats.kurtosis(bin_centers, fisher=True)

return stats_dict

def histogram_statistics(*images: np.ndarray, plot_histogram: bool = False, nbins: int = 256, legend_elem: List = []) -> List[Dict]:
"""
Get the statistics of multiple images.
Parameters:
plot_histograms: Plot the image histograms. Defaults to False.
bins: Number of histogram bins. Defaults to 256.
Returns:
dict: A dictionary of image metadata and histogram statistics.
Includes the shape, dtype, min, max, mean, median, variance, skewness, and kurtosis.
"""

img_stats = []
img_list = []

for image in images:
stats = _hist_stats(image, nbins)

print('-'*35)
print('Image statistics:')
print(f'\tShape: {stats["shape"]}')
print(f'\tData type: {stats["dtype"]}')
print(f'\tMin: {stats["min"]}, Max: {stats["max"]}, Mean: {stats["mean"]}\n')


img_stats.append(_hist_stats(image, nbins))
img_list.append(image.flatten())

if plot_histogram:
assert len(legend_elem) == len(images), 'Number of legend elements must match the number of images'
fig, ax = plt.subplots()
ax.hist(img_list, bins=nbins, density=True, histtype='step', fill=False)
ax.set_xlabel('Image Value', fontsize=16)
ax.set_ylabel('Probability Density', fontsize=16)
ax.grid(True)
ax.legend(legend_elem)

return img_stats
50 changes: 50 additions & 0 deletions examples/grayscale_filtering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from dpm_tools.io import download_file
from dpm_tools.metrics import histogram_statistics as hist_stats
import skimage.io
import numpy as np
import matplotlib.pyplot as plt

if __name__ == "__main__":
file_url1 = "https://www.digitalrocksportal.org/projects/211/images/126114/download/";
filename1 = "Project211_sandstone_slice_0801hr.png"
download_file(file_url1, filename1)

# image location, this is a lower resolution image (see project)
file_url2 = "https://www.digitalrocksportal.org/projects/211/images/126113/download/"
filename2 = "Project211_sandstone_slice_0801lr.png"
download_file(file_url2, filename2)
# CHANGE MADE - using one of the three channels
img_hr = skimage.io.imread(filename1)
img_hr = img_hr[:,:,0] #use only one channel of the three

img_lr = skimage.io.imread(filename2)
img_lr = img_lr[:,:,0] #use only one channel of the three

f = plt.figure(figsize=(24, 24))
f.add_subplot(221)
plt.imshow(img_hr) # if the image has three channels, default - and fake - color will appear when using imshow. Color scheme can easily be changed.
plt.title('higher res',fontsize=20)

f.add_subplot(222)
plt.imshow(img_lr) # if the image has three channels, default - and fake - color will appear when using imshow
plt.title('lower res',fontsize=20)

# location and length of an image subset in x, y directions
locx = 470
lenx = 90
locy = 440
leny = 90

f.add_subplot(223)
plt.imshow(img_hr[locx:(locx+lenx),locy:(locy+leny)])
plt.title('zoom in - higher res',fontsize=20)

f.add_subplot(224)
# note that // is floor division (so we get integers as a result)
plt.imshow(img_lr[locx // 4:(locx+lenx)//4,locy//4:(locy+leny)//4])
plt.title('zoom in - lower res', fontsize=20)

hist_stats(img_hr, img_lr, plot_histogram=True, nbins=128, legend_elem=["img_hr", "img_lr"])
plt.show()


1 change: 0 additions & 1 deletion examples/segmentation_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# print(segmented.dtype)
# srm_obj.segment()
segmented = srm(image[0].astype(np.uint16), Q=5.0)
print(segmented)

# Plot the result
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies = [
"scikit-fmm",
"connected-components-3d",
"pyfftw",
"wget",
"dpm-srm",
"dpm-srg"]

Expand Down

0 comments on commit 9bfe396

Please sign in to comment.