def get_dimension_only(imagefile): # get OME-XML and change the encoding to UTF-8 omexml = bioformats.get_omexml_metadata(imagefile) new_omexml = omexml.encode('utf-8') rdr = bioformats.get_image_reader(None, path=imagefile) # read total number of image series totalseries = rdr.rdr.getSeriesCount() # get dimensions for CTZXY md = bioformats.OMEXML(new_omexml) pixels = md.image(IMAGEID).Pixels SizeC = pixels.SizeC SizeT = pixels.SizeT SizeZ = pixels.SizeZ SizeX = pixels.SizeX SizeY = pixels.SizeY print 'Series: ', totalseries, 'T: ', SizeT, 'Z: ', SizeZ, 'C: ', SizeC, 'X: ', SizeX, 'Y: ', SizeY # usually the x-axis of an image is from left --> right and y from top --> bottom # in order to be compatible with numpy arrays XY are switched # for numpy arrays the 2st axis are columns (top --> down) = Y-Axis for an image sizes = [totalseries, SizeT, SizeZ, SizeC, SizeY, SizeX] return sizes
def make_url_image_reader(image_sets): last_url = None for url, series, index in image_sets: if url != last_url: rdr = bioformats.get_image_reader(None, url=url) last_url = url print "Loading %s" % path yield rdr.read(series.index, rescale=False)
def get_java_metadata_store(imagefile): if not VM_STARTED: start_jvm() if VM_KILLED: jvm_error() # get the actual image reader rdr = bioformats.get_image_reader(None, path=imagefile) # for "whatever" reason the number of total series can only be accessed here ... try: totalseries = np.int(rdr.rdr.getSeriesCount()) except: totalseries = 1 # in case there is only ONE series try: for sc in range(0, totalseries): rdr.rdr.setSeries(sc) resolutioncount = rdr.rdr.getResolutionCount() print('Resolution count for series #', sc, ' = ' + resolutioncount) for res in range(0, resolutioncount): rdr.rdr.setResolution(res) print('Resolution #', res, ' dimensions = ', rdr.getSizeX(), ' x ', rdr.getSizeY()) except: print('Multi-Resolution API not enabled yet.') series_dimensions = [] # cycle through all the series and check the dimensions for sc in range(0, totalseries): rdr.rdr.setSeries(sc) dimx = rdr.rdr.getSizeX() dimy = rdr.rdr.getSizeY() series_dimensions.append((dimx, dimy)) if len(series_dimensions) == 1: multires = False elif len(series_dimensions) > 1: if len(set(series_dimensions)) > 1: multires = True elif len(set(series_dimensions)) == 1: multires = False # rdr.rdr is the actual BioFormats reader. rdr handles its lifetime javametadata = jv.JWrapper(rdr.rdr.getMetadataStore()) imagecount = javametadata.getImageCount() imageIDs = [] for id in range(0, imagecount): imageIDs.append(id) rdr.close() return javametadata, totalseries, imageIDs, series_dimensions, multires
def __init__(self, dv_path, tag_path=None, cache_all=True, speak=True): self.dv_path = dv_path self.tag_path = tag_path self.cache_all = cache_all javabridge.start_vm(class_path=bioformats.JARS) if speak: print('Getting DV metadata....') self.meta_str = bioformats.get_omexml_metadata(path=self.dv_path) if speak: print('Solving metadata...') self.meta_xml = bioformats.omexml.OMEXML(self.meta_str) if cache_all: if speak: print('Caching all images') self.cache_image = self.__read_image() else: self.cache_image = np.empty(0) self.reader = bioformats.get_image_reader(None, self.dv_path) if tag_path is not None: tmp = sio.loadmat(tag_path)['tag'] if len(tmp.shape) < 3: tmp = np.reshape(tmp, newshape=(1, tmp.shape[0], tmp.shape[1])) self.tags = tmp.astype(np.int32) else: self.tags = np.empty(0)
def make_file_image_reader(image_sets): last_path = None for path, series, index in image_sets: if path != last_path: rdr = bioformats.get_image_reader(None, path=path) last_path = path print "Loading %s" % path yield rdr.read(series, index, rescale=False)
def get_java_metadata_store(imagefile): if not VM_STARTED: start_jvm() if VM_KILLED: jvm_error() rdr = bioformats.get_image_reader(None, path=imagefile) # for "whatever" reason the number of total series can only be accessed here ... try: totalseries = np.int(rdr.rdr.getSeriesCount()) except: totalseries = 1 # in case there is only ONE series # rdr.rdr is the actual BioFormats reader. rdr handles its lifetime jmd = jv.JWrapper(rdr.rdr.getMetadataStore()) imagecount = jmd.getImageCount() IMAGEID = imagecount - 1 rdr.close() return jmd, totalseries, IMAGEID
def get_dimension_only(imagefile, imageID=0): if not VM_STARTED: start_jvm() if VM_KILLED: jvm_error() rdr = bioformats.get_image_reader(None, path=imagefile) # read total number of image series totalseries = rdr.rdr.getSeriesCount() # get dimensions for CTZXY metadata = get_metadata_store(imagefile) pixels = metadata.image(imageID).Pixels SizeC = pixels.SizeC SizeT = pixels.SizeT SizeZ = pixels.SizeZ SizeX = pixels.SizeX SizeY = pixels.SizeY print('Series: ', totalseries) print('Size T: ', SizeT) print('Size Z: ', SizeZ) print('Size C: ', SizeC) print('Size X: ', SizeX) print('Size Y: ', SizeY) # usually the x-axis of an image is from left --> right and y from top --> bottom # in order to be compatible with numpy arrays XY are switched # for numpy arrays the 2st axis are columns (top --> down) = Y-Axis for an image sizes = [totalseries, SizeT, SizeZ, SizeC, SizeY, SizeX] rdr.close() return sizes
meta_data['x_size'] = x_range meta_data['y_size'] = y_range meta_data['z_size'] = z_range # Get the datatype of each pixel if pix_type == 'uint8': np_dtype = np.uint8 if pix_type == 'uint16': np_dtype = np.uint16 if depth == 'z': # Get the reader and read every image from the stack of the first image # series (series 0) in dimension order XYZC raw_data = np.empty([y_range, x_range, z_range, c_range], dtype=np.uint8) #raw_data = [] with bioformats.get_image_reader(key="cached_bf_reader", path=path_to_data, \ url=None) as reader: # Read the stack for every channel for c in range(c_range): # Read the whole stack for z in range(z_range): # Ignore the time raw_image = reader.read(c=c, z=z, t=0, series=None, index=None, \ rescale=False, wants_max_intensity=False, \ channel_names=None, XYWH=None) raw_data[:, :, z, c] = raw_image bioformats.release_image_reader("cached_bf_reader") bioformats.clear_image_reader_cache() reader.close() if depth == 't':
def imread(filename, channel_names=None): javabridge.start_vm(class_path=bioformats.JARS) reader = bioformats.get_image_reader(path=filename, key=0) n_series = reader.rdr.getSeriesCount() metadata_xml = bioformats.get_omexml_metadata( path=filename).encode('utf-8') img_metadata = bioformats.OMEXML(metadata_xml) bit_dtypes = {} bit_dtypes[8] = np.uint8 bit_dtypes[16] = np.uint16 img_series = {} for s in range(n_series)[:1]: reader.rdr.setSeries(s) img = np.array([[ reader.read(c=None, z=z, t=t) for z in xrange(reader.rdr.getSizeZ()) ] for t in xrange(reader.rdr.getSizeT())]) if reader.rdr.getSizeC() == 1: img = img[:, :, :, :, np.newaxis] # img = np.transpose(img,(0,4,2,3,1)) img = np.transpose(img, (1, 4, 3, 2, 0)) series_name = img_metadata.image(s).Name vx = get_float_attr(img_metadata.image(s).Pixels.node, "PhysicalSizeX") vy = get_float_attr(img_metadata.image(s).Pixels.node, "PhysicalSizeY") vz = get_float_attr(img_metadata.image(s).Pixels.node, "PhysicalSizeZ") vx = 1 if vx is None else vx vy = 1 if vy is None else vy vz = 1 if vz is None else vz print(vx, vy, vz) bits = get_int_attr( img_metadata.image(s).Pixels.node, "SignificantBits") if img.shape[0] > 1: for t in xrange(img.shape[0]): img_series[series_name + "_T" + str(t).zfill(2)] = {} for c in xrange(img.shape[1]): image = SpatialImage(img[t, c], voxelsize=(vx, vy, vz)) if (image.max() <= 1.0): image = image * (np.power(2, bits) - 1) image = image.astype(bit_dtypes[bits]) if channel_names is not None and len( channel_names) == img.shape[1]: channel_id = channel_names[c] else: channel_id = img_metadata.image(s).Pixels.Channel(c).ID if channel_id is None: channel_id = "C" + str(c) img_series[series_name + "_T" + str(t).zfill(2)][channel_id] = image if img.shape[1] == 1: img_series[series_name + "_T" + str(t).zfill(2)] = img_series[ series_name + "_T" + str(t).zfill(2)].values()[0] else: img_series[series_name] = {} for c in xrange(img.shape[1]): image = np.copy(img[0, c]) if (image.max() <= 1.0): image = image * (np.power(2, bits) - 1) image = image.astype(bit_dtypes[bits]) image = SpatialImage(image, voxelsize=(vx, vy, vz)) if channel_names is not None and len( channel_names) == img.shape[1]: channel_id = channel_names[c] else: channel_id = img_metadata.image(s).Pixels.Channel(c).Name if channel_id is None: channel_id = img_metadata.image(s).Pixels.Channel(c).ID if channel_id is None: channel_id = "C" + str(c) img_series[series_name][channel_id] = image if img.shape[1] == 1: img_series[series_name] = img_series[series_name].values()[0] if n_series == 1: return img_series.values()[0] else: return img_series
def get_tif_stack(filepath, series=0, depth='z', return_dim_order='YXZC'): """ This function assumes, that the Javabridge-VM is started and initialized with the bioformats jars: javabridge.start_vm(class_path=bioformats.JARS) To kill the vom use: javabridge.kill_vm() Parameters: filepath (string): Path to the multistack tif file series: (int): Number of the image series which shuld be read. Default = 0 time_step: Number of the time step which should be read. Default = 0 (NOT IMPLEMENTED) depth (string): 'z', when the depth is the z-axis or 't', when the depth is the t-axis bit_depth (integer): Power of two from the bit-depth of the file to be read. return_dim_order (string): In which order the raw data should be returned. XYZC, YXZC ... Returns: meta_data (dictionary): Metadata Dictionary of the tif file raw_data (numpy-array): Numpy-Array which contains the raw data of the image stack in the tif file """ # Get XML metadata of complete file xml_string = bioformats.get_omexml_metadata(filepath) ome_xml = bioformats.OMEXML(xml_string) # Read the metadata from the image -> series 0 iome = ome_xml.image(series) series_count = ome_xml.get_image_count() image_name = iome.get_Name() image_id = iome.get_ID() image_acquisition = iome.get_AcquisitionDate() # Geth the pixel meta data from the image ch_count = iome.Pixels.get_channel_count() dim_order = iome.Pixels.get_DimensionOrder() pic_id = iome.Pixels.get_ID() phy_x = iome.Pixels.get_PhysicalSizeX() phy_x_unit = iome.Pixels.get_PhysicalSizeXUnit() phy_y = iome.Pixels.get_PhysicalSizeY() phy_y_unit = iome.Pixels.get_PhysicalSizeYUnit() phy_z = iome.Pixels.get_PhysicalSizeZ() phy_z_unit = iome.Pixels.get_PhysicalSizeZUnit() pix_type = iome.Pixels.get_PixelType() plane_count = iome.Pixels.get_plane_count() c_range = iome.Pixels.get_SizeC() t_range = iome.Pixels.get_SizeT() x_range = iome.Pixels.get_SizeX() y_range = iome.Pixels.get_SizeY() z_range = iome.Pixels.get_SizeZ() # Make a dictionary out of the metadata meta_data = {} meta_data['series_count'] = series_count meta_data['image_name'] = image_name meta_data['image_id'] = image_id meta_data['image_acquisition'] = image_acquisition meta_data['channel_count'] = ch_count meta_data['original_dimension_order'] = dim_order meta_data['numpy_array_dimension_order'] = 'XYZC' meta_data['picture_id'] = pic_id meta_data['physical_size_x'] = phy_x meta_data['physical_size_x_unit'] = phy_x_unit meta_data['physical_size_y'] = phy_y meta_data['physical_size_y_unit'] = phy_y_unit meta_data['physical_size_z'] = phy_z meta_data['physical_size_z_unit'] = phy_z_unit meta_data['pixel_data_type'] = pix_type meta_data['plane_count'] = plane_count meta_data['channel_size'] = c_range meta_data['time_size'] = t_range meta_data['x_size'] = x_range meta_data['y_size'] = y_range meta_data['z_size'] = z_range # Get the datatype of each pixel if pix_type == 'uint8': np_dtype = np.uint8 if pix_type == 'uint16': np_dtype = np.uint16 if depth == 'z': # Allocate a numpy array for the given dimension raw_data = np.empty([y_range, x_range, z_range, c_range], dtype=np_dtype) # Get the reader and read every image from the stack of the first image # series (series 0) in dimension order XYZC with bioformats.get_image_reader(key="cached_bf_reader", path=filepath, \ url=None) as reader: # Read the stack for every channel for c in range(c_range): # Read the whole stack for z in range(z_range): # Ignore the time-axis raw_image = reader.read(c=c, z=z, t=0, series=None, index=None, \ rescale=False, wants_max_intensity=False, \ channel_names=None, XYWH=None) raw_data[:, :, z, c] = raw_image if depth == 't': # Allocate a numpy array for the given dimension raw_data = np.empty([y_range, x_range, t_range, c_range], dtype=np_dtype) # Get the reader and read every image from the stack of the first image # series (series 0) in dimension order XYZC with bioformats.get_image_reader(key="cached_bf_reader", path=filepath, \ url=None) as reader: # Read the stack for every channel for c in range(c_range): # Read the whole stack for t in range(t_range): # Ignore the z-axis raw_image = reader.read(c=c, z=0, t=t, series=None, index=None, \ rescale=False, wants_max_intensity=False, \ channel_names=None, XYWH=None) raw_data[:, :, t, c] = raw_image # Clear the cache and close the reader bioformats.release_image_reader("cached_bf_reader") bioformats.clear_image_reader_cache() reader.close() if return_dim_order == 'XYZC': return meta_data, np.transpose(raw_data, axes=(1, 0, 2, 3)) else: # YXZC return meta_data, raw_data
def findFocussedCZI(df, output_dir, method='BREN', imageSizeThreshXY=None, show=False): """ Find most focussed CZI image from dataframe of 'filepaths' to CZI image stacks of 96-well plate well images. params ------ df (DataFrame): pandas DataFrame containing 'filepath' column of full paths to CZI files output_dir (str): output directory path to save results method (str): focus measure algorithm supported methods ----------------- GLVA: Graylevel variance (Krotkov, 86) BREN: Brenner's (Santos, 97) imageSizeThreshXY (list/array) [int,int]: minimum threshold X,Y image size """ file_df_list = [] df = df.sort_values(by=['filepath']).reset_index(drop=True) n = len(df['filepath']) for f, file in enumerate(df['filepath']): print("\nProcessing file %d/%d (%.1f%%)" % (f + 1, n, ((f + 1) / n) * 100)) # extract metadata from filename file = str(file) fname, dname = os.path.basename(file), os.path.basename( os.path.dirname(file)) plateID = fname.split('.')[0] conc_mM = plateID.split("mM")[0].split("_")[-1] # get the actual image reader rdr = bioformats.get_image_reader(None, path=file) #with bioformats.ImageReader(path=file, url=None, perform_init=True) as reader: # image_arrays = reader.read(file) #image_arrays = czifile.imread(file) #image_arrays.shape # get total image series count try: # for "whatever" reason the number of total image series can only be accessed this way totalseries = int(rdr.rdr.getSeriesCount()) except: totalseries = 1 # in case there is only ONE series #totalseries = image_arrays.shape[1] #zSlices = image_arrays.shape[3] # OPTIONAL: Get metadata (obtain instrument info) #omeXMLObject = getMetadata(file) #meta = readMetadata(omeXMLObject) # parse the CZI file file_info = [] too_small_log = [] # Loop over wells (series) for sc in range(totalseries): # Set reader to series rdr.rdr.setSeries(sc) # img = np.array(image_arrays[0,sc,0,0,:,:,0]) # hor_profile = img.any(axis=0) # ver_profile = img.any(axis=1) # hor_first = np.where(hor_profile != 0)[0].min() # hor_last = np.where(hor_profile != 0)[0].max() # ver_first = np.where(ver_profile != 0)[0].min() # ver_last = np.where(ver_profile != 0)[0].max() # Filter small images if imageSizeThreshXY: x, y = rdr.rdr.getSizeX(), rdr.rdr.getSizeY() # x = hor_last - hor_first # y = ver_last - ver_first if (x <= imageSizeThreshXY[0] and y <= imageSizeThreshXY[1]): too_small_log.append(sc) else: # get number of z-slices zSlices = rdr.rdr.getImageCount() # Loop over z-slices for zc in range(zSlices): img = rdr.read(c=None, z=0, t=0, series=sc, index=zc, rescale=False) # img = np.array(image_arrays[0, sc, 0, zc, ver_first:ver_last, hor_first:hor_last, 0]) # plt.imshow(img); plt.pause(3); plt.close() # measure focus of raw image (uint16) fm = fmeasure(img, method) # store image info file_info.append([file, plateID, conc_mM, sc, zc, fm]) if len(too_small_log) > 0: print("WARNING: %d image series were omitted (image size too small)"\ % len(too_small_log)) # create dataframe from list of recorded data colnames = [ 'filepath', 'plateID', 'GFP_mM', 'seriesID', 'z_slice_number', 'focus_measure' ] file_df = pd.DataFrame.from_records(file_info, columns=colnames) # store file info file_df_list.append(file_df) # get images with max focus for each well/GFP concentration focussed_images_df = file_df[file_df['focus_measure'] == \ file_df.groupby(['seriesID'])['focus_measure']\ .transform(max)] print("%d most focussed images found." % focussed_images_df.shape[0]) # save most focussed images print("Saving most focussed images..") # Add dname to outDir when analysing multiple replicate folders at a time if df.shape[0] == len([i for i in df['filepath'] if dname in str(i)]): # We are analysing a single replicate folder outDir = os.path.join(output_dir, plateID) else: # We are analysing multiple replicate folders outDir = os.path.join(output_dir, dname, plateID) if not os.path.exists(outDir): os.makedirs(outDir) for i in range(focussed_images_df.shape[0]): if (i + 1) % 8 == 0: print("%d/%d" % (i + 1, focussed_images_df.shape[0])) # Extract image metadata from filename img_info = focussed_images_df.iloc[i] sc = img_info['seriesID'] zc = img_info['z_slice_number'] # We do NOT want to rescale images if comparing between them rdr.rdr.setSeries(sc) img = rdr.read(c=None, z=0, t=0, series=sc, index=zc, rescale=False) # img = image_arrays[0,sc,0,zc,:,:,0] assert img.dtype == np.uint16 # Convert image to 8-bit (Warning: some information will be lost) #import cv2 #img = cv2.convertScaleAbs(img, alpha=(255.0/65535.0)) #assert img.dtype == np.uint8 img = crop_image_nonzero(img) if show: plt.close('all') plt.imshow(img) plt.pause(5) # save as TIFF image outPath = os.path.join(outDir, plateID + '_s%dz%d' % (sc, zc) + '.tif') # Save as TIFF (bioformats) bioformats.write_image(pathname=outPath, pixels=img, pixel_type=bioformats.PT_UINT16) # Save TIF image as '.npy' for compatibility with ilastik software # np.save(outPath.replace('.tif','.npy'), # img[None, None, None, :, :], # allow_pickle=True) # concatenate dataframe from CZI file info df = pd.concat(file_df_list, axis=0, ignore_index=True) # save focus measures to file outDir = os.path.join(output_dir, 'focus_measures.csv') df.to_csv(outDir, index=False) focussed_images_df = df[df['focus_measure'] == \ df.groupby(['plateID','GFP_mM','seriesID'])['focus_measure']\ .transform(max)].reset_index(drop=True) return focussed_images_df
parser.add_argument('--file', metavar='file', help='file containing the image .lif extension') parser.add_argument('--size', metavar='size', help='which series ranges to extract') parser.add_argument( '--dest', metavar='dest', help='the numpy array format text file name as destination of extraction') # Reading the file args = parser.parse_args() # Converting range t = tuple([int(x) for x in args.size.split(",")]) window = range(*t) # Program Here javabridge.start_vm(class_path=bf.JARS) reader = bf.get_image_reader('r1', path="./180509_Permeabilisation_Test.lif") #meta = bf.get_omexml_metadata(args.file) #xmlome = bf.OMEXML(meta) #mdroot = ETree.fromstring(meta.encode('utf-8')) for i in window: n, m = reader.read(c=0, series=i, rescale=False, wants_max_intensity=True) np.savetxt(str(i) + args.dest, n) bf.clear_image_reader_cache() javabridge.kill_vm()
def open_loci_5d(input_file_path, output_file_path='', nt=-1, nc=-1, nz=-1, ti=0, ci=0, zi=0, series=0, logging_level='INFO', bioformats_logging='OFF'): """ Bioformats file reader. Warning: The data is expected to be in XYZCT order. :param input_file_path: Input file path (OME, OMETIFF, CZI, LIF, TIFF, LIF, ...) :param output_file_path: Output NPY file path. By default: input_file_path with NPY extension. :param nt: Number of time points :param nc: Number of channels :param nz: Number of slices :param ti: Initial time point :param ci: Initial channel :param zi: Initial slice :param logging_level: Level of logging: DEBUG, INFO, WARNING, ERROR :param bioformats_logging: Bioformats logging level :param series: Which series to open, if more than one :return: 5D ndarray in order XYZCT """ dir_data = os.path.dirname(input_file_path) logging.basicConfig(filename=os.path.join(dir_data, 'loci_reader.log'), level=logging_level) logging.info('Opening file {}...'.format(input_file_path)) loci_logging(bioformats_logging) reader = bioformats.get_image_reader(input_file_path, path=input_file_path) image_reader = reader.rdr nx = image_reader.getSizeX() ny = image_reader.getSizeY() nz_tot = image_reader.getSizeZ() nc_tot = image_reader.getSizeC() nt_tot = image_reader.getSizeT() if nz == -1: nz = nz_tot - zi if nc == -1: nc = nc_tot - ci if nt == -1: nt = nt_tot - ti if not (0 <= zi < nz_tot): logging.warning( 'The initial slice {} is not within the slices range 0-{}. Using central slice {} instead.' .format(zi, nz_tot - 1, int(nz_tot * 0.5))) zi = int(nz_tot * 0.5) if nz_tot < nz + zi: logging.warning( 'Total number of slices is {} and you chose {}. Using {} instead.'. format(nz_tot, nz + zi, nz_tot - zi)) nz = nz_tot - zi if not (0 <= ci < nc_tot): logging.warning( 'The initial channel {} is not within the channel range 0-{}. Using central slice {} instead.' .format(ci, nc_tot - 1, int(nc_tot * 0.5))) ci = int(nc_tot * 0.5) if nc_tot < nc + ci: logging.warning( 'Total number of channel is {} and you chose {}. Using {} instead.' .format(nc_tot, nc + ci, nc_tot - ci)) nc = nc_tot - ci if not (0 <= ti < nt_tot): logging.warning( 'The initial time point {} is not within the time-points range {}-{}. ' 'Using time point 0 instead.'.format(ti, 0, nt_tot - 1)) ti = 0 if nt_tot < nt + ti: logging.warning( 'Total number of time points is {} and you chose {}. Using {} instead.' .format(nt_tot, nt + ti, nt_tot - ti)) nt = nt_tot - ti if not (0 <= series < image_reader.getSeriesCount()): logging.warning( 'The series {} is not within the series scope 0-{}. Opening series 0 instead.' .format(series, image_reader.getSeriesCount())) series = 0 image_reader.getSeries(series) format_tool = bioformats.formatreader.make_format_tools_class() pixel_type = format_tool.getPixelTypeString(reader.rdr.getPixelType()) image5d = np.zeros([ny, nx, nz - zi, nc - ci, nt - ti], pixel_type) for t in np.arange(ti, nt + ti): logging.debug('Opening time point {} / {}'.format(t, nt)) for c in np.arange(ci, nc + ci): for z in np.arange(zi, nz + zi): image5d[..., z - zi, c - ci, t - ti] = reader.read(c=c, z=z, t=t, series=series, rescale=False) dir_data = os.path.dirname(input_file_path) filename = os.path.basename(input_file_path) if output_file_path == '': output_file_path = os.path.join(dir_data, filename[:filename.find(".")] + '.npy') np.save(output_file_path, image5d) logging.info('Successfully save file {}'.format(output_file_path))