From a7194b701b78f2dd530f9d623c162a23ee42c932 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Barbano <carlo.alberto.barbano@outlook.com>
Date: Thu, 9 Jan 2025 00:29:33 +0100
Subject: [PATCH 1/5] Bump version to 1.4.0

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index a8b3628..040a55e 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@
 
 setup(
     name='torchstain',
-    version='1.3.0',
+    version='1.4.0',
     description='Stain normalization tools for histological analysis and computational pathology',
     long_description=README,
     long_description_content_type='text/markdown',

From eeda0ec4d429294b28192bb08e9dcd6005f5b9f3 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Barbano <carlo.alberto.barbano@outlook.com>
Date: Thu, 9 Jan 2025 11:08:47 +0100
Subject: [PATCH 2/5] Update README.md

Update citation
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 00f2d4e..4e9b62a 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ Runtimes using the Macenko algorithm using different backends. Metrics were calc
 - [1] Macenko, Marc et al. "A method for normalizing histology slides for quantitative analysis." 2009 IEEE International Symposium on Biomedical Imaging: From Nano to Macro. IEEE, 2009.
 - [2] Reinhard, Erik et al. "Color transfer between images." IEEE Computer Graphics and Applications. IEEE, 2001.
 - [3] Roy, Santanu et al. "Modified Reinhard Algorithm for Color Normalization of Colorectal Cancer Histopathology Images". 2021 29th European Signal Processing Conference (EUSIPCO), IEEE, 2021.
-- [4] Ivanov, Desislav et al. "Multi-target stain normalization for histology slides". arXiv (preprint). 2024.
+- [4] Ivanov, Desislav et al. "Multi-target stain normalization for histology slides". 2nd International Workshop on Medical Optical Imaging and Virtual Microscopy Image Analysis (MOVI 2024), MICCAI. 2024.
 
 ## Citing
 

From 7b2620e625178c6c9a95ed0ff9970bd9097a8f17 Mon Sep 17 00:00:00 2001
From: Carlo Alberto Barbano <carlo.alberto.barbano@outlook.com>
Date: Thu, 9 Jan 2025 11:12:12 +0100
Subject: [PATCH 3/5] Update README.md [no ci]

fix tests badge
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4e9b62a..3e6dec7 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # torchstain
 
 [![License](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
-[![tests](https://github.com/EIDOSLAB/torchstain/workflows/tests/badge.svg)](https://github.com/EIDOSLAB/torchstain/actions)
+[![Full Tests](https://github.com/EIDOSLAB/torchstain/actions/workflows/tests_full.yml/badge.svg)](https://github.com/EIDOSLAB/torchstain/actions/workflows/tests_full.yml)
 [![Pip Downloads](https://img.shields.io/pypi/dm/torchstain?label=pip%20downloads&logo=python)](https://pypi.org/project/torchstain/)
 [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7692014.svg)](https://doi.org/10.5281/zenodo.7692014)
 

From f268a7848460fbc9ba093ec5ceb46a56f58ea7c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= <andrped94@gmail.com>
Date: Sat, 11 Jan 2025 12:02:21 +0100
Subject: [PATCH 4/5] Update base multitarget

---
 torchstain/base/normalizers/multitarget.py | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/torchstain/base/normalizers/multitarget.py b/torchstain/base/normalizers/multitarget.py
index 779b6df..495569b 100644
--- a/torchstain/base/normalizers/multitarget.py
+++ b/torchstain/base/normalizers/multitarget.py
@@ -1,6 +1,10 @@
-def MultiMacenkoNormalizer(backend='torch', **kwargs):
-    if backend == 'torch':
-        from torchstain.torch.normalizers.multitarget import MultiMacenkoNormalizer
-        return MultiMacenkoNormalizer(**kwargs)
+def MultiMacenkoNormalizer(backend="torch", **kwargs):
+    if backend == "numpy":
+        raise NotImplementedError("MultiMacenkoNormalizer is not implemented for NumPy backend")
+    elif backend == "torch":
+        from torchstain.torch.normalizers import TorchMultiMacenkoNormalizer
+        return TorchMultiMacenkoNormalizer(**kwargs)
+    elif backend == "tensorflow":
+        raise NotImplementedError("MultiMacenkoNormalizer is not implemented for TensorFlow backend")
     else:
-        raise Exception(f'Unsupported backend {backend}')
+        raise Exception(f"Unsupported backend {backend}")

From a52c85caba662a2a148b10c35640711df09ad487 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= <andrped94@gmail.com>
Date: Sat, 11 Jan 2025 12:05:35 +0100
Subject: [PATCH 5/5] Fixed usage bug of multitarget normalizer

---
 torchstain/base/normalizers/__init__.py     |  1 +
 torchstain/torch/normalizers/__init__.py    |  2 +-
 torchstain/torch/normalizers/multitarget.py | 17 +++++++++--------
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/torchstain/base/normalizers/__init__.py b/torchstain/base/normalizers/__init__.py
index a8f8bb7..ee38330 100644
--- a/torchstain/base/normalizers/__init__.py
+++ b/torchstain/base/normalizers/__init__.py
@@ -1,3 +1,4 @@
 from .he_normalizer import HENormalizer
 from .macenko import MacenkoNormalizer
+from .multitarget import MultiMacenkoNormalizer
 from .reinhard import ReinhardNormalizer
diff --git a/torchstain/torch/normalizers/__init__.py b/torchstain/torch/normalizers/__init__.py
index c7b3059..c508e04 100644
--- a/torchstain/torch/normalizers/__init__.py
+++ b/torchstain/torch/normalizers/__init__.py
@@ -1,3 +1,3 @@
 from torchstain.torch.normalizers.macenko import TorchMacenkoNormalizer
-from torchstain.torch.normalizers.multitarget import MultiMacenkoNormalizer
+from torchstain.torch.normalizers.multitarget import TorchMultiMacenkoNormalizer
 from torchstain.torch.normalizers.reinhard import TorchReinhardNormalizer
diff --git a/torchstain/torch/normalizers/multitarget.py b/torchstain/torch/normalizers/multitarget.py
index 88ffafc..5ed800a 100644
--- a/torchstain/torch/normalizers/multitarget.py
+++ b/torchstain/torch/normalizers/multitarget.py
@@ -1,16 +1,17 @@
 import torch
 from torchstain.torch.utils import cov, percentile
+
 """
 Implementation of the multi-target normalizer from the paper: https://arxiv.org/pdf/2406.02077
 """
-class MultiMacenkoNormalizer:
-    def __init__(self, norm_mode='avg-post'):
+class TorchMultiMacenkoNormalizer:
+    def __init__(self, norm_mode="avg-post"):
         self.norm_mode = norm_mode
         self.HERef = torch.tensor([[0.5626, 0.2159],
                                    [0.7201, 0.8012],
                                    [0.4062, 0.5581]])
         self.maxCRef = torch.tensor([1.9705, 1.0308])
-        self.updated_lstsq = hasattr(torch.linalg, 'lstsq')
+        self.updated_lstsq = hasattr(torch.linalg, "lstsq")
         
     def __convert_rgb2od(self, I, Io, beta):
         I = I.permute(1, 2, 0)
@@ -59,7 +60,7 @@ def __compute_matrices_single(self, I, Io, alpha, beta):
         return HE, C, maxC
 
     def fit(self, Is, Io=240, alpha=1, beta=0.15):
-        if self.norm_mode == 'avg-post':
+        if self.norm_mode == "avg-post":
             HEs, _, maxCs = zip(*(
                 self.__compute_matrices_single(I, Io, alpha, beta)
                 for I in Is
@@ -67,7 +68,7 @@ def fit(self, Is, Io=240, alpha=1, beta=0.15):
 
             self.HERef = torch.stack(HEs).mean(dim=0)
             self.maxCRef = torch.stack(maxCs).mean(dim=0)
-        elif self.norm_mode == 'concat':
+        elif self.norm_mode == "concat":
             ODs, ODhats = zip(*(
                 self.__convert_rgb2od(I, Io, beta)
                 for I in Is
@@ -83,7 +84,7 @@ def fit(self, Is, Io=240, alpha=1, beta=0.15):
             maxCs = torch.stack([percentile(C[0, :], 99), percentile(C[1, :], 99)])
             self.HERef = HE
             self.maxCRef = maxCs
-        elif self.norm_mode == 'avg-pre':
+        elif self.norm_mode == "avg-pre":
             ODs, ODhats = zip(*(
                 self.__convert_rgb2od(I, Io, beta)
                 for I in Is
@@ -100,7 +101,7 @@ def fit(self, Is, Io=240, alpha=1, beta=0.15):
             maxCs = torch.stack([percentile(C[0, :], 99), percentile(C[1, :], 99)])
             self.HERef = HE
             self.maxCRef = maxCs
-        elif self.norm_mode == 'fixed-single' or self.norm_mode == 'stochastic-single':
+        elif self.norm_mode == "fixed-single" or self.norm_mode == "stochastic-single":
             # single img
             self.HERef, _, self.maxCRef = self.__compute_matrices_single(Is[0], Io, alpha, beta)
         else:
@@ -127,4 +128,4 @@ def normalize(self, I, Io=240, alpha=1, beta=0.15, stains=True):
             E[E > 255] = 255
             E = E.T.reshape(h, w, c).int()
 
-        return Inorm, H, E
\ No newline at end of file
+        return Inorm, H, E