Source code for ewoksndreg.intensities.metrics
from typing import Optional
import numpy
from scipy.stats import entropy
[docs]
def normalized_mutual_information_metric(
image1: numpy.ndarray,
image2: numpy.ndarray,
bins: int = 100,
image1_mask: Optional[numpy.ndarray] = None,
image2_mask: Optional[numpy.ndarray] = None,
) -> float:
"""
Given two images, the mutual information between the images is calculated based on an histogramm with 'bins' bins
Masks can be given to exclude some parts of the images from the histogramm calculation
Alternatively, two stacks of images can be given, where the mutual information per image pair is calculated and summed.
"""
if image1.ndim == 2 and image2.ndim == 2:
if image1_mask is not None:
if image1.shape() != image1_mask.shape:
raise ValueError("Masks must have same shape as image")
image1 = image1[image1_mask]
if image2_mask is not None:
if image2.shape() != image2_mask.shape:
raise ValueError("Masks must have same shape as image")
image2 = image2[image2_mask]
img1 = image1[numpy.logical_not(numpy.isnan(image2))]
img2 = image2[numpy.logical_not(numpy.isnan(image2))]
joint_bins, edgex, edgey = numpy.histogram2d(
img1.flatten(), img2.flatten(), bins=bins
)
image1_entropy = entropy(numpy.sum(joint_bins, axis=0))
image2_entropy = entropy(numpy.sum(joint_bins, axis=1))
joint_entropy = entropy(joint_bins.flatten())
if joint_entropy != 0:
return (image1_entropy + image2_entropy) / joint_entropy
else:
return 0
elif image1.ndim == 3 and image2.ndim == 3:
NME_sum = 0
for i in range(image1.shape[1]):
NME_sum += normalized_mutual_information_metric(
image1[i],
image2[i],
bins=bins,
image1_mask=image1_mask[i],
image2_mask=image2_mask[i],
)
return NME_sum
else:
raise ValueError("ndim of both images must be 2 or 3")
[docs]
def mean_squared_metric(image1: numpy.ndarray, image2: numpy.ndarray) -> numpy.float64:
mean = numpy.nanmean((image1 - image2) ** 2, dtype=numpy.float64)
return mean if not numpy.isnan(mean) else numpy.finfo(numpy.float64).max