OmniPathWithInterTaskAttention / utils_color_norm.py
TETSU0701's picture
Update utils_color_norm.py
0cfff72 verified
Raw
History Blame Contribute Delete
2.38 kB
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
"""
Macenko stain normalization — Hugging Face Space compatible version
Removed dependency on SPAMS (no native libraries required).
"""
def standardize_brightness(I):
p = np.percentile(I, 95)
return np.clip(I * 255.0 / p, 0, 255).astype(np.uint8)
def remove_zeros(I):
mask = (I == 0)
I[mask] = 1
return I
def RGB_to_OD(I):
I = remove_zeros(I)
return -np.log((I.astype(np.float32) + 1) / 256)
def OD_to_RGB(OD):
return np.clip(255 * np.exp(-OD), 0, 255).astype(np.uint8)
def normalize_rows(A):
return A / np.linalg.norm(A, axis=1)[:, None]
def get_stain_matrix(I, beta=0.15, alpha=1):
OD = RGB_to_OD(I).reshape((-1, 3))
OD = OD[(OD > beta).any(axis=1), :]
_, V = np.linalg.eigh(np.cov(OD, rowvar=False))
V = V[:, [2, 1]]
if V[0, 0] < 0: V[:, 0] *= -1
if V[0, 1] < 0: V[:, 1] *= -1
That = np.dot(OD, V)
phi = np.arctan2(That[:, 1], That[:, 0])
minPhi = np.percentile(phi, alpha)
maxPhi = np.percentile(phi, 100 - alpha)
v1 = np.dot(V, np.array([np.cos(minPhi), np.sin(minPhi)]))
v2 = np.dot(V, np.array([np.cos(maxPhi), np.sin(maxPhi)]))
HE = np.array([v1, v2])
return normalize_rows(HE)
def get_concentrations(I, stain_matrix):
OD = RGB_to_OD(I).reshape((-1, 3))
return np.linalg.lstsq(stain_matrix.T, OD.T, rcond=None)[0].T
class macenko_normalizer(object):
def __init__(self):
self.stain_matrix_target = np.array(
[[0.5626, 0.2159], [0.7201, 0.8012], [0.4062, 0.5581]], dtype=np.float32).T
self.target_concentrations = None
def fit(self, target):
target = standardize_brightness(target)
self.stain_matrix_target = get_stain_matrix(target)
self.target_concentrations = get_concentrations(target, self.stain_matrix_target)
def transform(self, I):
I = standardize_brightness(I)
stain_matrix_source = get_stain_matrix(I)
source_concentrations = get_concentrations(I, stain_matrix_source)
maxC_source = np.percentile(source_concentrations, 99, axis=0).reshape((1, 2))
maxC_target = np.array([1.9705, 1.0308], dtype=float).reshape((1, 2))
source_concentrations *= maxC_target / maxC_source
return OD_to_RGB(np.dot(source_concentrations, self.stain_matrix_target).reshape(I.shape))