def test_sims_extra_masses(self): tiff.write(self.filename, self.float_image) with warnings.catch_warnings(record=True) as warns: image = tiff.read(self.filename, masses=[1, 2, 6]) expected_image = tiff.read(self.filename).slice_image([1, 2]) self.assertEqual(expected_image, image) messages = [str(w.message) for w in warns] self.assertTrue('Requested masses not found in file: [6]' in messages)
def test_sims_and_sed(self): tiff.write(self.filename, self.image, sed=SED) image = tiff.read(self.filename) self.assertEqual(image, self.image) sims, sed, optical, label = tiff.read(self.filename, sed=True, optical=True, label=True) self.assertEqual(sims, self.image) np.testing.assert_array_equal(sed, SED) self.assertIsNone(optical) self.assertIsNone(label)
def test_sims_extra_targets(self): tiff.write(self.filename, self.float_image) target = self.float_image.targets[1] with warnings.catch_warnings(record=True) as warns: image = tiff.read(self.filename, targets=['Target0', target]) expected_image = tiff.read(self.filename).slice_image([target]) self.assertEqual(expected_image, image) messages = [str(w.message) for w in warns] self.assertTrue( 'Requested targets not found in file: [\'Target0\']' in messages)
def test_sims_only(self): tiff.write(self.filename, self.image) image = tiff.read(self.filename) self.assertEqual(image, self.image) sims, sed, optical, label = tiff.read(self.filename, sed=True, optical=True, label=True) self.assertEqual(sims, self.image) self.assertIsNone(sed) self.assertIsNone(optical) self.assertIsNone(label) self.assertEqual(image.data.dtype, np.uint16)
def test_read_optical_and_label_only(self): tiff.write(self.filename, self.image, optical=CAROUSEL) optical, label = tiff.read(self.filename, sims=False, optical=True, label=True) np.testing.assert_array_equal(optical, CAROUSEL) np.testing.assert_array_equal(label, LABEL) optical_only = tiff.read(self.filename, sims=False, optical=True) np.testing.assert_array_equal(optical_only, CAROUSEL) label_only = tiff.read(self.filename, sims=False, label=True) np.testing.assert_array_equal(label_only, LABEL)
def load_imgs_from_mibitiff(mibitiff_paths, channels=None, load_axis="fovs"): """Load images from a series of MIBItiff files. This function takes a set of MIBItiff files and load the images into an xarray. The type used to store the images will be the same as that of the MIBIimages stored in the MIBItiff files. Args: mibitiff_paths: list of MIBItiff files to load. channels: optional list of channels to load. Defaults to `None`, in which case, all channels in the first MIBItiff are used. load_axis: axis that images will get loaded onto. Must be one of ["fovs", "stacks"]. Returns: img_xr: xarray with shape [fovs, tifs, x_dim, y_dim] """ # sanity check if not np.isin(load_axis, ["fovs", "stacks"]): raise ValueError( "Invalid value for load_axis, must be one of [fovs, stacks]") # if no channels specified, get them from first MIBItiff file if channels is None: channel_tuples = tiff.read(mibitiff_paths[0]).channels channels = [channel_tuple[1] for channel_tuple in channel_tuples] # extract point name from file name fovs = [ mibitiff_path.split(os.sep)[-1].split('_')[0] for mibitiff_path in mibitiff_paths ] # extract images from MIBItiff file img_data = [] for mibitiff_path in mibitiff_paths: img_data.append(tiff.read(mibitiff_path)[channels]) img_data = np.stack(img_data, axis=0) # create xarray with image data img_xr = xr.DataArray(img_data, coords=[ fovs, range(img_data[0].data.shape[0]), range(img_data[0].data.shape[1]), channels ], dims=[load_axis, "rows", "cols", "channels"]) return img_xr
def test_write_float32_from_float64(self): float64_image = mi.MibiImage(DATA.astype(np.float64), CHANNELS, **METADATA) tiff.write(self.filename, float64_image, multichannel=True) image = tiff.read(self.filename) np.testing.assert_equal(image.data, DATA) self.assertEqual(image.data.dtype, np.float32)
def test_write_float_tiff(self): tiff.write(self.filename, self.image, multichannel=True, write_float=True) image = tiff.read(self.filename) self.assertEqual(image.data.dtype, np.float32) np.testing.assert_equal(image.data, self.image.data.astype(np.float32))
def test_write_uint16_from_uint8(self): uint8_image = mi.MibiImage( np.random.randint(0, 256, (32, 32, 5), dtype=np.uint8), CHANNELS, **METADATA) tiff.write(self.filename, uint8_image, multichannel=True) image = tiff.read(self.filename) np.testing.assert_equal(image.data, uint8_image.data.astype(np.uint16)) self.assertEqual(image.data.dtype, np.uint16)
def test_sims_and_sed_and_optical_and_label(self): tiff.write(self.filename, self.image, sed=SED, optical=CAROUSEL) image, sed, optical, label = tiff.read(self.filename, sed=True, optical=True, label=True) self.assertEqual(image, self.image) np.testing.assert_array_equal(sed, SED) np.testing.assert_array_equal(optical, CAROUSEL) np.testing.assert_array_equal(label, LABEL)
def test_write_uint16_from_float32_dtype_np_uint16(self): tiff.write(self.filename, self.float_image, multichannel=True, dtype=np.uint16) image = tiff.read(self.filename) self.assertEqual(image.data.dtype, np.uint16) np.testing.assert_equal(image.data, self.float_image.data.astype(np.uint16))
def test_write_float32_from_float32_tiff_dtype_none_non_ascii(self): tiff.write(self.filename, self.float_image_non_ascii, multichannel=True) image = tiff.read(self.filename) self.assertEqual(image.data.dtype, np.float32) np.testing.assert_equal( image.data, self.float_image_non_ascii.data.astype(np.float32)) np.testing.assert_equal(image.channels, self.float_image_non_ascii.channels)
def test_sort_channels_before_writing(self): # Unordered indices: [2, 0, 4, 1, 3] unordered_channels = ((3, 'Target3'), (1, 'Target1'), (5, 'Target5'), (2, 'Target2'), (4, 'Target4')) unordered_data = np.stack([DATA[:, :, 2], DATA[:, :, 0], DATA[:, :, 4], DATA[:, :, 1], DATA[:, :, 3]], axis=2) unordered_image = mi.MibiImage(unordered_data, unordered_channels, **METADATA) tiff.write(self.filename, unordered_image) image = tiff.read(self.filename) self.assertEqual(image, self.image)
def get_mibi_image(self, image_id): """Gets image data from MIBItracker and creates a MibiImage instance. Args: image_id: The integer id of an image. Return: A MibiImage instance of the requested image. """ image_info = self.get('images/{}/'.format(image_id)).json() tiff_path = '/'.join((image_info['run']['path'], image_info['folder'], 'summed_image.tiff')) tiff_data = self.download_file(tiff_path) return tiff.read(tiff_data)
def merge_mibitiffs(input_folder, out=None): """Merges a folder of single-channel MIBItiff files into a single MIBItiff. Args: input_folder: Path to a folder containing MIBItiff files. While these files may be single-channel, they are assumed to have accurate and consistent MIBI metadata. out: Optionally, a path to a location for saving the combined TIFF. If not specified, defaults to 'combined.tiff' inside the input folder. """ pattern = re.compile(r'.+\.tiff?$') paths = [ os.path.join(input_folder, f) for f in os.listdir(input_folder) if re.match(pattern, f.lower()) ] merged = tiff.read(paths[0]) for path in paths[1:]: image = tiff.read(path) merged.append(image) if out is None: out = os.path.join(input_folder, 'combined.tiff') tiff.write(out, merged, multichannel=True)
def test_write_single_channel_tiffs(self): basepath = os.path.split(self.filename)[0] tiff.write(basepath, self.image, multichannel=False) for i, (_, channel) in enumerate(CHANNELS): formatted = util.format_for_filename(channel) filename = os.path.join(basepath, '{}.tiff'.format(formatted)) tif = tiff.read(filename) np.testing.assert_equal(np.squeeze(tif.data), DATA[:, :, i]) self.assertTupleEqual(tif.channels, (CHANNELS[i], )) self.assertEqual(tif.data.dtype, np.uint16)
def test_load_imgs_from_mibitiff(): mibitiff_files = [ os.path.join( os.path.dirname(os.path.realpath(__file__)), "..", "..", "data", "example_dataset", "input_data", "input_data_TIFF", "Point8_RowNumber0_Depth_Profile0-MassCorrected-Filtered.tiff") ] channels = ["HH3", "Membrane"] data_xr = data_utils.load_imgs_from_mibitiff(mibitiff_files, channels) assert (data_xr.dims == ("fovs", "rows", "cols", "channels")) assert (data_xr.fovs == "Point8") assert (data_xr.rows == range(1024)).all() assert (data_xr.cols == range(1024)).all() assert (data_xr.channels == channels).all() np.testing.assert_array_equal(data_xr.values[0], (tiff.read( mibitiff_files[0]))[channels].data)
def plot_1_fov(file_name, l_channel, ax=None, file_id=''): """Plot a selection of channels for one FoV. Parameters ---------- file_name : str Path to MIBItiff file of the FoV to use for plotting. l_channel : list List of channels to plot. ax : `~matplotlib.axes.Axes`, optional Axes of the figure for the plot. file_id : str, optional File ID to use in the title for the plots. Returns ------- ax : `~matplotlib.axes.Axes` Axes of the figure containing the plots. """ print() print(f'Plotting file: {file_name}') print(f' File ID: {file_id}') image = tiff.read(file_name) # TODO: allow target name anonymization!!! # loop over channels for channel, axis in zip(l_channel, ax): # plot image print(f' Channel: {channel}') im = image[channel] counts = im.sum() plot_image(im, ax=axis, title=str(file_id) + ': ' + str(channel) + ' ' + str("{:.2e}".format(counts)), brighten_image=True) if GRAPH_DEBUG > 3: plt.show() # wait until image is closed if GRAPH_DEBUG > 2: plt.show() # don't leave at the end return ax
def test_load_imgs_from_mibitiff_all_channels(): mibitiff_files = [ os.path.join( os.path.dirname(os.path.realpath(__file__)), "..", "..", "data", "example_dataset", "input_data", "input_data_TIFF", "Point8_RowNumber0_Depth_Profile0-MassCorrected-Filtered.tiff") ] data_xr = data_utils.load_imgs_from_mibitiff(mibitiff_files, channels=None) assert (data_xr.dims == ("fovs", "rows", "cols", "channels")) assert (data_xr.fovs == "Point8") assert (data_xr.rows == range(1024)).all() assert (data_xr.cols == range(1024)).all() exected_channels = [ "Background", "BetaCatenin", "BetaTubulin", "CD20", "CD3", "CD4", "CD45", "CD8", "CD9", "ECadherin", "ER", "GLUT1", "HER2", "HH3", "HLA_Class_1", "Ki67", "LaminAC", "Membrane", "NaK ATPase", "PanKeratin", "SMA", "Vimentin" ] assert (data_xr.channels == exected_channels).all() np.testing.assert_array_equal(data_xr.values[0], (tiff.read(mibitiff_files[0])).data)
def plot_1_fov(file_name, l_channel, ax=None, file_id=''): """Plot the channels for one FoV.""" print() print(f'Plotting file: {file_name}') print(f' File ID: {file_id}') # load input input_folder = '~/IONpath/data/temp' input_folder = os.path.expanduser( input_folder) # expand home dir into string file_path = os.path.join(input_folder, file_name) image = tiff.read(file_path) #import IPython; IPython.embed() #print(f'image metadata:\n{image.metadata()}') #print(f'image channels:\n{image.channels}') # loop over channels for channel, axis in zip(l_channel, ax): # plot image print(f' Channel: {channel}') im = image[channel] counts = im.sum() #import IPython; IPython.embed() plot_image(im, ax=axis, title=str(file_id) + ': ' + str(channel) + ' ' + str("{:.2e}".format(counts))) if GRAPH_DEBUG > 3: plt.show() # wait until image is closed if GRAPH_DEBUG > 2: plt.show() # don't leave at the end return ax
def test_read_sed_only(self): tiff.write(self.filename, self.image, sed=SED) sed = tiff.read(self.filename, sims=False, sed=True) np.testing.assert_array_equal(sed, SED)
a_masses = np.arange(10, 220) a_masses.sort() print(f'a_masses = {a_masses}') # dark plot style plt.style.use('dark_background') # loop over FOVs for fov in df_fov_list['FOV Name']: print(f'\nGoing for FOV {fov}') # open input images image_path = os.path.join(input_path, f'{fov}.tiff') print(f'file: {image_path}') image = tiff.read(image_path) print(f'channels {sorted(image.channels)}') # sorted by mass if SAVE_OUTPUT: output_path_fov = os.path.join(output_path, fov) print(f'Creating output folder {output_path_fov}') os.makedirs(output_path_fov) # loop over channels for ch_order, channel in enumerate(sorted(image.channels)): mass = channel[0] target = channel[1] if mass in a_masses: # selected channels print() print(f'\nchannel {ch_order}: mass {mass}, target {target}')
def load_imgs_from_mibitiff(data_dir, mibitiff_files=None, channels=None, delimiter=None, dtype='int16'): """Load images from a series of MIBItiff files. This function takes a set of MIBItiff files and load the images into an xarray. The type used to store the images will be the same as that of the MIBIimages stored in the MIBItiff files. Args: data_dir (str): directory containing MIBItiffs mibitiff_files (list): list of MIBItiff files to load. If None, all MIBItiff files in data_dir are loaded. channels (list): optional list of channels to load. Defaults to `None`, in which case, all channels in the first MIBItiff are used. delimiter (str): optional delimiter-character/string which separate fov names from the rest of the file name. Defaults to None dtype (str/type): optional specifier of image type. Overwritten with warning for float images Returns: img_xr (xr.DataArray): xarray with shape [fovs, x_dim, y_dim, channels] """ if not mibitiff_files: mibitiff_files = iou.list_files(data_dir, substrs=['.tif']) # extract fov names w/ delimiter agnosticism fovs = iou.extract_delimited_names(mibitiff_files, delimiter=delimiter) mibitiff_files = [ os.path.join(data_dir, mt_file) for mt_file in mibitiff_files ] test_img = io.imread(mibitiff_files[0], plugin='tifffile') # check to make sure that float dtype was supplied if image data is float data_dtype = test_img.dtype if np.issubdtype(data_dtype, np.floating): if not np.issubdtype(dtype, np.floating): warnings.warn( f"The supplied non-float dtype {dtype} was overwritten to {data_dtype}, " f"because the loaded images are floats") dtype = data_dtype # if no channels specified, get them from first MIBItiff file if channels is None: channel_tuples = tiff.read(mibitiff_files[0]).channels channels = [channel_tuple[1] for channel_tuple in channel_tuples] # extract images from MIBItiff file img_data = [] for mibitiff_file in mibitiff_files: img_data.append(tiff.read(mibitiff_file)[channels]) img_data = np.stack(img_data, axis=0) img_data = img_data.astype(dtype) # create xarray with image data img_xr = xr.DataArray(img_data, coords=[ fovs, range(img_data[0].data.shape[0]), range(img_data[0].data.shape[1]), channels ], dims=["fovs", "rows", "cols", "channels"]) return img_xr
def open_tiff(file_name): return tiff.read(str(files[0]))
def test_sims_selected_targets(self): tiff.write(self.filename, self.float_image) image = tiff.read(self.filename, targets=self.float_image.targets[1:3]) expected_image = tiff.read(self.filename).slice_image(CHANNELS[1:3]) self.assertEqual(expected_image, image)
def test_sims_selected_masses_and_targets(self): tiff.write(self.filename, self.float_image) with self.assertRaises(ValueError): tiff.read(self.filename, masses=self.float_image.masses[1:2], targets=self.float_image.targets[2:3])
def test_sims_no_selected_found(self): tiff.write(self.filename, self.float_image) with self.assertRaises(ValueError): tiff.read(self.filename, targets=['do', 'not', 'exist'])
def test_read_with_invalid_return_types(self): tiff.write(self.filename, self.image) with self.assertRaises(ValueError): tiff.read(self.filename, sims=False)
def test_read_wrong_software_tag(self): with warnings.catch_warnings(): warnings.filterwarnings('ignore', message='.*low contrast image.*') io.imsave(self.filename, DATA) with self.assertRaises(ValueError): tiff.read(self.filename)
def main(): """Main function. Define a list of FoV files and list of channels. A large canvas is created with plots of each channel for each FoV. Each row in the canvas represents one FoV and each column represents a channel. Activate the global variable 'SAVE' in order to save the figure as a png file. """ data_path = os.path.expanduser('~/common/path/to/data') l_file_name = [] l_file_label = [] print() print('file 1: MIBI/O MIBItiff file') tiff_file = os.path.join(data_path, 'file1/specific/path/to/file.tiff') image = tiff.read(tiff_file) print(f'metadata {image.metadata}') print(f'channels {image.channels}') l_file_name.append(tiff_file) l_file_label.append('file') print() print('file 2: MATLAB combined MIBItiff file') # note: the matlab pipeline does not produce MIBItiffs, so the converter # has to be used first (see mibitracker-client/mibidata/combine_tiffs.py) input_folder = os.path.join(data_path, 'file2/specific/path/to/folder/with/TIFs') run_path = os.path.join(data_path, 'file2/specific/path/to/run_file.xml') point = 'Point0' panel_path = os.path.join(data_path, 'file2/specific/path/to/panel_file.csv') slide = '0' size = 500 #um combine_tiffs.create_mibitiffs(input_folder, run_path, point, panel_path, slide, size) # now the combined tiff file can be used to proceed as before tiff_file = os.path.join(input_folder, 'combined.tiff') image = tiff.read(tiff_file) print(f'metadata {image.metadata}') print(f'channels {image.channels}') l_file_name.append(tiff_file) l_file_label.append('matlab_file') l_channel = [89, 113, 115, 128, 146, 197] # l_target = ['89', '113', '115', 'Xe128', '146', '197'] # not used for now # TODO: function that takes the panel and a list of masses and returns a # list of the corresponding targets!!! n_files_to_plot = len(l_file_name) n_channels_to_plot = len(l_channel) # TODO: allow target name anonymization!!! figsize = 6 # inch fig, ax = plt.subplots(n_files_to_plot, n_channels_to_plot, figsize=(figsize * n_channels_to_plot, figsize * n_files_to_plot)) # loop over files and produce the plots count_files = 0 for tiff_file_name, file_label in zip(l_file_name, l_file_label): if count_files < n_files_to_plot: #plot_1_fov(tiff_file_name, l_channel, ax[count_files], # file_id=count_files) plot_1_fov(tiff_file_name, l_channel, ax[count_files], file_id=file_label) count_files += 1 # save ouptut if SAVE: output_file_name = 'plot_compare_extraction.png' print() print(f'Saving image to {output_file_name}') plt.savefig(output_file_name) if GRAPH_DEBUG > 1: plt.show() # don't leave at the end