|
| 1 | +# Adapted from CryoSegNet |
| 2 | +# https://github.com/jianlin-cheng/CryoSegNet |
| 3 | + |
| 4 | +import numpy as np |
| 5 | +import mrcfile |
| 6 | +import cv2 |
| 7 | +from numpy.fft import fft2, ifft2 |
| 8 | +from scipy.signal import gaussian |
| 9 | + |
| 10 | +def transform(image): |
| 11 | + i_min = image.min() |
| 12 | + i_max = image.max() |
| 13 | + |
| 14 | + image = ((image - i_min)/(i_max - i_min)) * 255 |
| 15 | + return image.astype(np.uint8) |
| 16 | + |
| 17 | + |
| 18 | +def standard_scaler(image): |
| 19 | + kernel_size = 9 |
| 20 | + image = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0) |
| 21 | + mu = np.mean(image) |
| 22 | + sigma = np.std(image) |
| 23 | + image = (image - mu)/sigma |
| 24 | + image = transform(image).astype(np.uint8) |
| 25 | + return image |
| 26 | + |
| 27 | +def contrast_enhancement(image): |
| 28 | + enhanced_image = cv2.fastNlMeansDenoising(image, None, h=10, templateWindowSize=7, searchWindowSize=21) |
| 29 | + |
| 30 | + return enhanced_image |
| 31 | + |
| 32 | + |
| 33 | +def gaussian_kernel(kernel_size = 3): |
| 34 | + h = gaussian(kernel_size, kernel_size / 3).reshape(kernel_size, 1) |
| 35 | + h = np.dot(h, h.transpose()) |
| 36 | + h /= np.sum(h) |
| 37 | + return h |
| 38 | + |
| 39 | +def wiener_filter(img, kernel, K): |
| 40 | + kernel /= np.sum(kernel) |
| 41 | + dummy = np.copy(img) |
| 42 | + dummy = fft2(dummy) |
| 43 | + kernel = fft2(kernel, s = img.shape) |
| 44 | + kernel = np.conj(kernel) / (np.abs(kernel) ** 2 + K) |
| 45 | + dummy = dummy * kernel |
| 46 | + dummy = np.abs(ifft2(dummy)) |
| 47 | + return dummy |
| 48 | + |
| 49 | +def clahe(image): |
| 50 | + clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(16,16)) |
| 51 | + |
| 52 | + # Apply CLAHE to the image |
| 53 | + img_equalized = clahe.apply(transform(image)) |
| 54 | + return img_equalized |
| 55 | + |
| 56 | +def guided_filter(input_image, guidance_image, radius=20, epsilon=0.1): |
| 57 | + # Convert images to float32 |
| 58 | + input_image = input_image.astype(np.float32) / 255.0 |
| 59 | + guidance_image = guidance_image.astype(np.float32) / 255.0 |
| 60 | + |
| 61 | + # Compute mean values of the guidance image and input image |
| 62 | + mean_guidance = cv2.boxFilter(guidance_image, -1, (radius, radius)) |
| 63 | + mean_input = cv2.boxFilter(input_image, -1, (radius, radius)) |
| 64 | + |
| 65 | + # Compute correlation and covariance of the guidance and input images |
| 66 | + mean_guidance_input = cv2.boxFilter(guidance_image * input_image, -1, (radius, radius)) |
| 67 | + covariance_guidance_input = mean_guidance_input - mean_guidance * mean_input |
| 68 | + |
| 69 | + # Compute squared mean of the guidance image |
| 70 | + mean_guidance_sq = cv2.boxFilter(guidance_image * guidance_image, -1, (radius, radius)) |
| 71 | + variance_guidance = mean_guidance_sq - mean_guidance * mean_guidance |
| 72 | + |
| 73 | + # Compute weights and mean of the weights |
| 74 | + a = covariance_guidance_input / (variance_guidance + epsilon) |
| 75 | + b = mean_input - a * mean_guidance |
| 76 | + mean_a = cv2.boxFilter(a, -1, (radius, radius)) |
| 77 | + mean_b = cv2.boxFilter(b, -1, (radius, radius)) |
| 78 | + |
| 79 | + # Compute the filtered image |
| 80 | + output_image = mean_a * guidance_image + mean_b |
| 81 | + |
| 82 | + return transform(output_image) |
| 83 | + |
| 84 | + |
| 85 | +def denoise(image_path): |
| 86 | + kernel = gaussian_kernel(kernel_size = 9) |
| 87 | + image = mrcfile.read(image_path) |
| 88 | + image = image.T |
| 89 | + image = np.rot90(image) |
| 90 | + normalized_image = standard_scaler(np.array(image)) |
| 91 | + contrast_enhanced_image = contrast_enhancement(normalized_image) |
| 92 | + weiner_filtered_image = wiener_filter(contrast_enhanced_image, kernel, K = 30) |
| 93 | + clahe_image = clahe(weiner_filtered_image) |
| 94 | + guided_filter_image = guided_filter(clahe_image, weiner_filtered_image) |
| 95 | + |
| 96 | + return guided_filter_image |
| 97 | + |
0 commit comments