Spaces:
Runtime error
Runtime error
| 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)) | |