from ..objective_function import ObjectiveFunc
import numpy as np
from skimage import metrics
__all__ = ["ImgApprox", "ImgEntropy", "ImgStd"]
[docs]
class ImgApprox(ObjectiveFunc):
def __init__(self, img_dim, reference, mode=None, img_name="", diff_func="MSE", name=None):
self.img_dim = tuple(img_dim) + (3,)
self.size = img_dim[0] * img_dim[1] * 3
self.reference = reference.resize((img_dim[0], img_dim[1]))
self.reference = np.asarray(self.reference)[:, :, :3].astype(np.uint8)
if name is None:
if img_name == "":
name = "Image approximation"
else:
name = f'Approximating "{img_name}"'
self.diff_func = diff_func
if mode is None:
if diff_func in ["MSE", "MAE"]:
mode = "min"
else:
mode = "max"
super().__init__(self.size, mode=mode, lower_bound=0, upper_bound=256, name=name, vectorized=True)
[docs]
def objective(self, solution):
error = np.zeros(solution.shape[0])
image_size = np.prod(solution.shape[1:])
match self.diff_func:
case "MSE":
error = np.ndarray.astype(np.sum((solution - self.reference) ** 2, axis=(1, 2, 3)) / image_size, float)
case "MAE":
error = np.ndarray.astype(np.sum(np.abs(solution - self.reference), axis=(1, 2, 3)) / image_size, float)
case "SSIM":
for idx, s in enumerate(solution):
for s_ch, ref_ch in zip(s.transpose((2, 0, 1)), self.reference.transpose((2, 0, 1))):
error[idx] += metrics.structural_similarity(s_ch, ref_ch)
error[idx] /= 3
case "NMI":
for idx, s in enumerate(solution):
for s_ch, ref_ch in zip(s.transpose((2, 0, 1)), self.reference.transpose((2, 0, 1))):
error[idx] += metrics.normalized_mutual_information(s_ch, ref_ch, bins=32)
error[idx] /= 3
return error
[docs]
class ImgStd(ObjectiveFunc):
def __init__(self, img_dim, mode=None):
self.size = img_dim[0] * img_dim[1] * 3
if mode is None:
mode = "max"
super().__init__(self.size, mode=mode, lower_bound=0, upper_bound=256, name="Image standard deviation")
[docs]
def objective(self, solution):
solution_color = solution.reshape([3, -1])
return solution_color.std(axis=1).mean()
[docs]
class ImgEntropy(ObjectiveFunc):
def __init__(self, img_dim, nbins=10, mode=None):
self.size = img_dim[0] * img_dim[1] * 3
self.nbins = nbins
if mode is None:
mode = "max"
super().__init__(self.size, mode=mode, lower_bound=0, upper_bound=256, name="Image entropy", vectorized=False)
[docs]
def objective(self, solution):
solution_channels = solution.reshape([3, -1])
img_hists = [np.histogram(solution_channels[i], bins=np.linspace(0, 256, self.nbins))[0] for i in range(3)]
img_hists = np.array(img_hists) / solution_channels.shape[1]
img_hists_no_zeros = img_hists
img_hists_no_zeros[img_hists == 0] = 1
return np.sum(-img_hists * np.log(img_hists_no_zeros))