Source code for topostats.statistics
"""Function for calculating statistics about a whole image, for example number of grains or surface roughness."""
import numpy as np
import pandas as pd
from topostats.logs.logs import setup_logger, LOGGER_NAME
LOGGER = setup_logger(LOGGER_NAME)
[docs]
def image_statistics(
image: np.ndarray,
filename: str,
pixel_to_nm_scaling: float,
results_df: pd.DataFrame,
) -> pd.DataFrame:
"""Calculate statistics pertaining to the whole image, for example the size of the image in pixels and
metres, the root-mean-squared roughness and the grains per metre squared.
Parameters
----------
image: np.ndarray
Numpy 2D image array of the image to calculate stats for.
filename: str
The name of the file being processed.
pixel_to_nm_scaling: float
Float of the scaling factor between pixels and nanometres.
results_df: pd.DataFrame
Pandas DataFrame of statistics pertaining to individual grains including from grainstats and
dna tracing.
Returns
-------
dict
Dictionary of image statistics.
"""
image_stats = {
"Image": filename,
"image_size_x_m": None,
"image_size_y_m": None,
"image_area_m2": None,
"image_size_x_px": image.shape[1],
"image_size_y_px": image.shape[0],
"image_area_px2": None,
"grains_number_above": None,
"grains_per_m2_above": None,
"grains_number_below": None,
"grains_per_m2_below": None,
"rms_roughness": None,
}
# Calculate dimensions of the image
image_stats["image_size_x_m"] = image.shape[1] * pixel_to_nm_scaling * 1e-9
image_stats["image_size_y_m"] = image.shape[0] * pixel_to_nm_scaling * 1e-9
image_stats["image_area_m2"] = image_stats["image_size_x_m"] * image_stats["image_size_y_m"]
image_stats["image_area_px2"] = image_stats["image_size_x_px"] * image_stats["image_size_y_px"]
# Calculate the RMS roughness of the sample on the flattened image.
image_stats["rms_roughness"] = roughness_rms(image=image) * 1e-9
# Calculate image stats relating to grain statistics. Note that the existence of any of these stats
# is not guaranteed
try:
image_stats["grains_number_below"] = results_df["threshold"].value_counts().get("below", 0)
image_stats["grains_per_m2_below"] = image_stats["grains_number_below"] / image_stats["image_area_m2"]
except KeyError:
pass
try:
image_stats["grains_number_above"] = results_df["threshold"].value_counts().get("above", 0)
image_stats["grains_per_m2_above"] = image_stats["grains_number_above"] / image_stats["image_area_m2"]
except KeyError:
pass
image_stats_df = pd.DataFrame([image_stats])
image_stats_df.set_index("Image", inplace=True)
return image_stats_df
[docs]
def roughness_rms(image: np.ndarray) -> float:
"""Calculate the root-mean-square roughness of a heightmap image.
Parameters
----------
image: np.ndarray
2D numpy array of heightmap data to calculate the roughness of
Returns:
float
The RMS roughness of the input array.
"""
return np.sqrt(np.mean(np.square(image)))