def to_ometiff(self, channel_clips, sample, json_file): channel_data = {} channels = [] images = [] ometif_path = '{}.ome.tif'.format(json_file.name.replace('.json', '')) s3_target = open('s3_target.txt').read().strip() for (channel, clip) in channel_clips: channel_data[channel] = { 'sample': sample, 'tileSource': 'https://s3.amazonaws.com/{}/linnarsson/'.format(s3_target) + 'linnarsson.tiles/linnarsson.images.{}/'.format(channel) + '{}.dzi'.format(channel) } channels.append(channel) array = self.scale_sample(channel=channel, sample=sample, max_allowed=256, clip=float(clip)).astype(np.uint8) images.append(array) json.dump(channel_data, json_file, indent=2) image = np.transpose(np.dstack(tuple(images))) image = np.expand_dims(image, axis=0) image = np.expand_dims(image, axis=0) channels = [tup[0] for tup in channel_clips] omexml = self.get_omexml(pixel_array=image, channels=channels, name='linnarsson', pixel_type='uint8') io.write_ometiff(ometif_path, image, str(omexml))
import numpy as np from pathlib import Path import datetime def create_omexml(image_array, channel_names): metadata = omexmlClass.OMEXML() image = metadata.image() image.AcquisitionDate = datetime.datetime.now().isoformat() pixels = image.Pixels pixels.SizeX = image_array.shape[2] pixels.SizeY = image_array.shape[1] pixels.SizeC = image_array.shape[0] pixels.PixelType = str(image_array.dtype) pixels.channel_count = len(channel_names) for i, name in enumerate(channel_names): pixels.Channel(i).Name = name return f'<?xml version="1.0" encoding="UTF-8"?>\n{metadata}' if __name__ == "__main__": arr = np.arange(3 * 50 * 50).reshape((3, 50, 50)) outfile = "dataset.ome.tif" channel_names = ["0", "1", "2"] omexml_string = create_omexml(arr, channel_names) io.write_ometiff(outfile, arr, omexml_string)
def execute(filepath, separator=';', filter_method='none', filter_size=3, threshold_method='triangle', min_objectsize=1000, max_holesize=100, saveformat='ome.tiff'): """Main function that executed the workflow. :param filepath: file path of the CZI image :type filepath: tsr :param separator: sepeartor for the CSV table, defaults to ';' :type separator: str, optional :param filter_method: smoothing filer, defaults to 'none' :type filter_method: str, optional :param filter_size: kernel size or radius of filter element, defaults to 3 :type filter_size: int, optional :param threshold_method: threshold method, defaults to 'triangle' :type threshold_method: str, optional :param min_objectsize: minimum object size, defaults to 1000 :type min_objectsize: int, optional :param max_holesize: maximum object size, defaults to 100 :type max_holesize: int, optional :param saveformat: format to save the segmented image, defaults to 'ome.tiff' :type saveformat: str, optional :return: outputs :rtype: dict """ print('--------------------------------------------------') print('FilePath : ', filepath) print(os.getcwd()) print('File exists : ', os.path.exists(filepath)) print('--------------------------------------------------') # define name for figure to be saved filename = os.path.basename(filepath) # get the metadata from the czi file md, additional_mdczi = imf.get_metadata(filepath) # to make it more readable extravt values from metadata dictionary stageX = md['SceneStageCenterX'] stageY = md['SceneStageCenterY'] # define columns names for dataframe cols = ['S', 'T', 'Z', 'C', 'Number'] objects = pd.DataFrame(columns=cols) # optional dipslay of "some" results - empty list = no display show_image = [0] # scalefactor to read CZI sf = 1.0 # index for channel - currently only single channel images are supported ! chindex = 0 # define maximum object sizes max_objectsize = 1000000000 # define save format for mask adapt_dtype_mask = True dtype_mask = np.int8 # check if it makes sense if max_holesize > min_objectsize: min_objectsize = max_holesize # read the czi mosaic image czi = CziFile(filepath) # get the shape of the data using aicspylibczi print('Dimensions : ', czi.dims) print('Size : ', czi.size) print('Shape : ', czi.dims_shape()) print('IsMosaic : ', czi.is_mosaic()) # read the mosaic pixel data mosaic = czi.read_mosaic(C=0, scale_factor=sf) print('Mosaic Shape :', mosaic.shape) # get the mosaic as NumPy.Array - must fit im memory !!! image2d = np.squeeze(mosaic, axis=0) md['SizeX_readmosaic'] = image2d.shape[1] md['SizeY_readmosaic'] = image2d.shape[0] # create the savename for the OME-TIFF if saveformat == 'ome.tiff': savename_seg = filename.split('.')[0] + '.ome.tiff' if saveformat == 'tiff': savename_seg = filename.split('.')[0] + '.tiff' # initialize empty dataframe results = pd.DataFrame() # main loop over all T - Z - C slices for s in progressbar.progressbar(range(md['SizeS']), redirect_stdout=True): for t in range(md['SizeT']): for z in range(md['SizeZ']): values = {'S': s, 'T': t, 'Z': z, 'C': chindex, 'Number': 0} # preprocessing - filter the image if filter_method == 'none' or filter_method == 'None': image2d_filtered = image2d if filter_method == 'median': image2d_filtered = median(image2d, selem=disk(filter_size)) if filter_method == 'gauss': image2d_filtered = gaussian(image2d, sigma=filter_size, mode='reflect') # threshold image binary = sgt.autoThresholding(image2d_filtered, method=threshold_method) # Remove contiguous holes smaller than the specified size mask = morphology.remove_small_holes(binary, area_threshold=max_holesize, connectivity=1, in_place=True) # remove small objects mask = morphology.remove_small_objects(mask, min_size=min_objectsize, in_place=True) # clear the border mask = segmentation.clear_border(mask, bgval=0, in_place=True) # label the objects mask = measure.label(binary) # adapt pixel type of mask if adapt_dtype_mask: mask = mask.astype(dtype_mask, copy=False) # measure region properties to_measure = ('label', 'area', 'centroid', 'bbox') # measure the specified parameters store in dataframe props = pd.DataFrame( measure.regionprops_table( mask, intensity_image=image2d, properties=to_measure)).set_index('label') # filter objects by size props = props[(props['area'] >= min_objectsize) & (props['area'] <= max_objectsize)] # add well information for CZI metadata try: props['WellId'] = md['Well_ArrayNames'][s] props['Well_ColId'] = md['Well_ColId'][s] props['Well_RowId'] = md['Well_RowId'][s] except (IndexError, KeyError) as error: # Output expected ImportErrors. print('Key not found:', error) print('Well Information not found. Using S-Index.') props['WellId'] = s props['Well_ColId'] = s props['Well_RowId'] = s # add plane indices props['S'] = s props['T'] = t props['Z'] = z props['C'] = chindex # count the number of objects values['Number'] = props.shape[0] # update dataframe containing the number of objects objects = objects.append(pd.DataFrame(values, index=[0]), ignore_index=True) results = results.append(props, ignore_index=True) # make sure the array as 5D of order (T, Z, C, X, Y) to write an correct OME-TIFF mask = imf.expand_dims5d(mask, md) # write the OME-TIFF suing apeer-ometiff-library io.write_ometiff(savename_seg, mask, omexml_string=None) # rename columns in pandas datatable results.rename(columns={ 'bbox-0': 'ystart', 'bbox-1': 'xstart', 'bbox-2': 'yend', 'bbox-3': 'xend' }, inplace=True) # calculate the bbox width in height in [pixel] and [micron] results['bbox_width'] = results['xend'] - results['xstart'] results['bbox_height'] = results['yend'] - results['ystart'] results['bbox_width_scaled'] = results['bbox_width'] * md['XScale'] results['bbox_height_scaled'] = results['bbox_height'] * md['XScale'] # calculate the bbox center StageXY results['bbox_center_stageX'], results[ 'bbox_center_stageY'] = bbox2stageXY( image_stageX=stageX, image_stageY=stageY, sizeX=md['SizeX'], sizeY=md['SizeY'], scale=md['XScale'], xstart=results['xstart'], ystart=results['ystart'], bbox_width=results['bbox_width'], bbox_height=results['bbox_height']) # show results print(results) print('Done.') # write the CSV data table print('Write to CSV File : ', filename) csvfile = os.path.splitext(filename)[0] + '_planetable.csv' results.to_csv(csvfile, sep=separator, index=False) # set the outputs outputs = {} outputs['segmented_image'] = savename_seg outputs['objects_table'] = csvfile return outputs
ignore_index=True) results = results.append(props, ignore_index=True) image_counter += 1 # optional display of results if image_counter - 1 in show_image: print('Well:', props['WellId'].iloc[0], 'Index S-C:', s, chindex, 'Objects:', values['Number']) ax = vst.plot_segresults(image2d, mask, props, add_bbox=True) # make sure the array as 5D of order (T, Z, C, X, Y) to write an correct OME-TIFF mask = imf.expand_dims5d(mask, md) # write the OME-TIFF suing apeer-ometiff-library io.write_ometiff(savename, mask, omexml_string=None) # rename columns in pandas datatable results.rename(columns={ 'bbox-0': 'ystart', 'bbox-1': 'xstart', 'bbox-2': 'yend', 'bbox-3': 'xend' }, inplace=True) # calculate the bbox width in height in [pixel] and [micron] results['bbox_width'] = results['xend'] - results['xstart'] results['bbox_height'] = results['yend'] - results['ystart'] results['bbox_width_scaled'] = results['bbox_width'] * md['XScale'] results['bbox_height_scaled'] = results['bbox_height'] * md['XScale']