R2-V2 / preprocessing.py
j-morano's picture
Add bv model (safetensors) with standalone inference pipeline
bb8c6c9
Raw
History Blame Contribute Delete
4.75 kB
from pathlib import Path
import argparse
import numpy as np
from PIL import Image
import scipy.ndimage as ndimage
import skimage.io as io
from skimage.exposure import equalize_adapthist
from scipy.interpolate import interp1d
from skimage.morphology import disk
def crop_center(img, cropx, cropy):
y, x = img.shape[0], img.shape[1]
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy, startx:startx+cropx]
def to_0_1(img):
interp_fun = interp1d([img.min(), img.max()], [0.0, 1.0])
return interp_fun(img)
def enhance_image(img, mask, int_format=False, disk_size=5):
"""Enhance an image using the method described in the paper.
Args:
img (uint8 np.ndarray): Image to enhance
mask (np.ndarray): ROI mask of the image to enhance
Returns:
Enhanced image
"""
# Read image and its corresponding mask
if isinstance(img, str) or isinstance(img, Path):
img = io.imread(img)[..., :3]
if isinstance(mask, str) or isinstance(mask, Path):
mask = io.imread(mask)
if len(img.shape) == 3:
if img.shape[2] > 3:
img = img[:, :, :3]
if len(mask.shape) == 3:
mask = np.sum(mask[:, :, :3], axis=2)
img = img / 255
# mask = np.where(mask > (255//2), 255, 0)
mask = np.where(mask > 0.5, 1, 0)
# Copy original image
img_copy = img.copy()
# Convert to PIL format
zoomed_image = Image.fromarray(np.uint8(img_copy*255))
# Enlarge image
zoomed_image = zoomed_image.resize(
(int(img_copy.shape[1]*1.15), int(img_copy.shape[0]*1.15)),
Image.BICUBIC # type: ignore
)
# To numpy array type
zoomed_image = np.array(zoomed_image)
# Crop image to original size (zoom result)
zoomed_image = crop_center(zoomed_image, img_copy.shape[1],
img_copy.shape[0])
# Convert image from 0-255 format to 0.0-1.0 format
zoomed_image = zoomed_image / 255.0
# Create circular kernel for mask erosion
kernel = disk(disk_size)
# Erode mask
mask = ndimage.binary_erosion(mask, kernel)
# Convert boolean array to float array
mask = mask * 1.0 # type: ignore
img_copy[mask < 1.0] = 0.0
# Create RGB mask (same mask for all channels)
mask = np.stack((mask, mask, mask), axis=2)
composed_image = mask.copy()
composed_image[mask == 1.0] = img_copy[mask == 1.0]
composed_image[mask < 1.0] = zoomed_image[mask < 1.0]
filtered_image = ndimage.gaussian_filter(composed_image, sigma=(10, 10, 0))
subtracted_image = composed_image - filtered_image
subtracted_image[mask < 1.] = 0.
enhanced_image = subtracted_image/np.std(subtracted_image)
enhanced_image = to_0_1(enhanced_image)
enhanced_image[mask < 1.] = 0.
mask = mask[:, :, 0]
if int_format:
enhanced_image *= 255
enhanced_image = enhanced_image.astype(np.uint8)
mask *= 255
mask = mask.astype(np.uint8)
return enhanced_image, mask
def preprocess_img(img, mask):
img_enh, mask_enh = enhance_image(img, mask, int_format=True)
img_enh_clahe = equalize_adapthist(img_enh, clip_limit=0.01)
return img_enh_clahe, mask_enh
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Preprocess images and masks.')
parser.add_argument(
'-c', '--cfp_path',
type=str,
required=True,
help='Path to the images to preprocess',
)
parser.add_argument(
'-m', '--mask_path',
type=str,
required=True,
help='Path to the masks to preprocess',
)
parser.add_argument(
'-p', '--out_pre_path',
type=str,
required=True,
help='Path to save the preprocessed images',
)
parser.add_argument(
'-o', '--out_mask_path',
type=str,
required=True,
help='Path to save the preprocessed masks',
)
args = parser.parse_args()
cfp_path = Path(args.cfp_path)
mask_path = Path(args.mask_path)
out_pre_path = Path(args.out_pre_path)
out_mask_path = Path(args.out_mask_path)
for cfp_fn in sorted(cfp_path.iterdir()):
print(cfp_fn)
mask_fn = mask_path / cfp_fn.name
img_enh_clahe, mask_enh = preprocess_img(cfp_fn, mask_fn)
img_enh_clahe = (img_enh_clahe * 255).astype('uint8')
mask_enh = (mask_enh > 127).astype('uint8') * 255
out_pre_fn = out_pre_path / cfp_fn.name
out_pre_fn.parent.mkdir(parents=True, exist_ok=True)
io.imsave(out_pre_fn, img_enh_clahe)
out_mask_fn = out_mask_path / cfp_fn.name
out_mask_fn.parent.mkdir(parents=True, exist_ok=True)
io.imsave(out_mask_fn, mask_enh)