Source code for MSIght.refactor_interpolation

# -*- coding: utf-8 -*-
"""
Created on Fri Nov 15 17:05:38 2024

@author: lafields2
"""

import pyimzml.ImzMLParser
import tifffile
import matplotlib.pyplot as plt
import numpy as np
import cv2
import os


[docs]def interpolate_MSI(filename,image_path,msi_image,smoothed_image,output_directory,sample_name): """ Interpolates an MSI image to match the dimensions of a corresponding H&E image. Parameters ---------- filename : str Path to the .imzML file containing MSI data. image_path : str Path to the corresponding H&E image file (TIFF format). msi_image : numpy.ndarray The MSI image to be resized. smoothed_image : numpy.ndarray The smoothed and binarized H&E image used for cropping. output_directory : str Directory where the resized MSI image will be saved. sample_name : str Name used for labeling the saved output file. Returns ------- cropped_image : numpy.ndarray The cropped H&E image after binarization and thresholding. resized_msi_image : numpy.ndarray The resized MSI image matching the cropped H&E image's dimensions. Notes ----- - Extracts image dimensions from the TIFF file and the .imzML file. - Applies a binary mask to the smoothed H&E image. - Determines cropping boundaries based on tissue presence. - Resizes the MSI image to match the cropped H&E image's dimensions using linear interpolation. - Saves the resized MSI image as a PNG file. """ parser = pyimzml.ImzMLParser.ImzMLParser(filename) # Get dimensions x_dimension = parser.imzmldict['max count of pixels x'] y_dimension = parser.imzmldict['max count of pixels y'] x_resolution = None y_resolution = None tif_file = image_path with tifffile.TiffFile(tif_file) as tif: # Read the TIFF file and get metadata x_dimension = tif.pages[0].shape[1] y_dimension = tif.pages[0].shape[0] binarized_image = smoothed_image binarized_image[binarized_image > 0] = 1 # Ensure the image is binary (0 or 255) # Calculate row and column sums row_sum = np.sum(binarized_image, axis=1) # Sum along rows col_sum = np.sum(binarized_image, axis=0) # Sum along columns crop_threshold = 15 # Determine crop boundaries top_crop = np.argmax(row_sum > crop_threshold) # Top boundary bottom_crop = len(row_sum) - np.argmax(row_sum[::-1] > crop_threshold) # Bottom boundary left_crop = np.argmax(col_sum > crop_threshold) # Left boundary right_crop = len(col_sum) - np.argmax(col_sum[::-1] > crop_threshold) # Right boundary cropped_image = binarized_image[top_crop:bottom_crop, left_crop:right_crop] # Crop the image dimensions = cropped_image.shape dimensions = cropped_image.shape x_dimension = dimensions[1] y_dimension = dimensions[0] resized_msi_image = cv2.resize(msi_image, (x_dimension, y_dimension), interpolation=cv2.INTER_LINEAR) # Resize MSI image to match dimensions of microscopy image plt.figure(figsize=(10, 8)) plt.imshow(resized_msi_image, cmap='viridis') plt.colorbar(label='Intensity') title = 'Resized MSI image with linear interpolation' plt.title(title) #fig_outpath = output_directory + '\\' + sample_name + '_MSI_composite_image_all_mz.png' fig_outpath = os.path.join(output_directory, f"{sample_name}_MSI_composite_image_all_mz.png") plt.savefig(fig_outpath,bbox_inches='tight') return cropped_image,resized_msi_image
[docs]def interpolate_and_visualize(filename, image_path, msi_image, smoothed_image, output_directory, sample_name, original_areas_to_zoom): """ Interpolates an MSI image to match the H&E image dimensions and visualizes different interpolation methods. Parameters ---------- filename : str Path to the .imzML file containing MSI data. image_path : str Path to the corresponding H&E image file (TIFF format). msi_image : numpy.ndarray The MSI image to be resized. smoothed_image : numpy.ndarray The smoothed and binarized H&E image used for cropping. output_directory : str Directory where the visualization output will be saved. sample_name : str Name used for labeling the saved output file. original_areas_to_zoom : dict Dictionary containing areas to zoom in as tuples (x1, y1, x2, y2). Returns ------- cropped_image : numpy.ndarray The cropped H&E image after binarization and thresholding. resized_msi_image : numpy.ndarray The resized MSI image matching the cropped H&E image's dimensions. Notes ----- - Extracts image dimensions from the .imzML file and the TIFF file. - Binarizes the H&E image and determines cropping boundaries. - Adjusts zoom areas to match the resized MSI image. - Compares multiple interpolation methods: Bilinear, Bicubic, Nearest Neighbor, and Lanczos. - Displays and saves the visualization as a PNG file. """ parser = pyimzml.ImzMLParser.ImzMLParser(filename) original_msi_width = parser.imzmldict['max count of pixels x'] original_msi_height = parser.imzmldict['max count of pixels y'] with tifffile.TiffFile(image_path) as tif: # Read the TIFF file and get metadata x_dimension = tif.pages[0].shape[1] y_dimension = tif.pages[0].shape[0] binarized_image = smoothed_image binarized_image[binarized_image > 0] = 1 row_sum = np.sum(binarized_image, axis=1) col_sum = np.sum(binarized_image, axis=0) crop_threshold = 15 top_crop = np.argmax(row_sum > crop_threshold) bottom_crop = len(row_sum) - np.argmax(row_sum[::-1] > crop_threshold) left_crop = np.argmax(col_sum > crop_threshold) right_crop = len(col_sum) - np.argmax(col_sum[::-1] > crop_threshold) cropped_image = binarized_image[top_crop:bottom_crop, left_crop:right_crop] dimensions = cropped_image.shape x_dimension = dimensions[1] y_dimension = dimensions[0] # Calculate scaling factors scale_x = x_dimension / original_msi_width scale_y = y_dimension / original_msi_height # Adjust the original zoom areas to match the resized image adjusted_areas_to_zoom = { name: ( int(x1 * scale_x), int(y1 * scale_y), int(x2 * scale_x), int(y2 * scale_y) ) for name, (x1, y1, x2, y2) in original_areas_to_zoom.items()} interpolation_methods = { 'Bilinear': cv2.INTER_LINEAR, 'Bicubic': cv2.INTER_CUBIC, 'NearestNeighbor': cv2.INTER_NEAREST, 'Lanczos': cv2.INTER_LANCZOS4} fig, axes = plt.subplots(len(adjusted_areas_to_zoom), len(interpolation_methods), figsize=(20, 10)) for col, (method_name, interpolation) in enumerate(interpolation_methods.items()): if method_name == 'Original': resized_msi_image = msi_image else: resized_msi_image = cv2.resize(msi_image, (x_dimension, y_dimension), interpolation=interpolation) for row, area_name in enumerate(adjusted_areas_to_zoom): x1, y1, x2, y2 = adjusted_areas_to_zoom[area_name] zoomed_image = resized_msi_image[y1:y2, x1:x2] ax = axes[row, col] img_plot = ax.imshow(zoomed_image, cmap='jet', aspect='auto') ax.imshow(zoomed_image, cmap='jet', aspect='auto') ax.set_title(f"{method_name} - {area_name}") ax.axis('off') plt.colorbar(img_plot, ax=ax, orientation='vertical') plt.suptitle('Interpolation Methods Comparison') plt.tight_layout(rect=[0, 0.03, 1, 0.95]) #fig_outpath = output_directory + '\\' + sample_name + '_interpolation_comparison.png' fig_outpath = os.path.join(output_directory, f"{sample_name}_interpolation_comparison.png") plt.savefig(fig_outpath, bbox_inches='tight') plt.show() return cropped_image, resized_msi_image