def threshold(imPlus, edgeThreshold=2500): mask = Duplicator().run(imPlus) mask_stk = mask.getStack() # First, we threshold based on edges IJ.setThreshold(mask, edgeThreshold, 100000, "No Update") for i in range(mask.getImageStackSize()): mask_stk.getProcessor(i + 1).findEdges() IJ.run(mask, "Make Binary", "method=Default background=Default black") # Now, we need to clean up the binary images morphologically IJ.run(mask, "Dilate", "stack") IJ.run(mask, "Fill Holes", "stack") IJ.run(mask, "Erode", "stack") IJ.run(mask, "Erode", "stack") # Finally, remove the small particles stk = ImageStack(mask.getWidth(), mask.getHeight()) p = PA(PA.SHOW_MASKS, 0, None, 200, 100000) p.setHideOutputImage(True) for i in range(mask_stk.getSize()): mask.setSliceWithoutUpdate(i + 1) p.analyze(mask) mmap = p.getOutputImage() stk.addSlice(mmap.getProcessor()) mask.setStack(stk) mask.setSliceWithoutUpdate(1) mask.setTitle(mask_title(imPlus.getTitle())) mask.show() return mask
def correct_drift(img1, img2, display_cc=False): ''' Returns two ImagePlus objects that are drift corrected to each other. The first image is not changed. The second image is a new image that is shifted using ImageJ's Translate. :param img1: The reference image. :param img2: The image to be shifted. :param display_cc: Activate displaying the CrossCorrelation image (default False). ''' img1_cc, img2_cc = cc.scale_to_power_of_two([img1, img2]) result = cc.perform_correlation(img1_cc, img2_cc) x_off, y_off = cc.get_shift(result) # style after maximum detection if not display_cc: result.hide() else: result.copyScale(img1) cc.style_cc(result) if x_off == 0 and y_off == 0: print('No drift has been detected.') return img1, img2 title = img2.getTitle() img2_dk = Duplicator().run(img2) img2_dk.setTitle('DK-' + title) IJ.run(img2_dk, 'Translate...', 'x=%d y=%d interpolation=None' % (-x_off, -y_off)) img2_dk.copyScale(img2) img2_dk.show() return img1, img2_dk
def gfp_analysis(imp, file_name, output_folder): """perform analysis based on gfp intensity thresholding""" cal = imp.getCalibration() channel_imps = ChannelSplitter.split(imp) gfp_imp = channel_imps[0] gfp_imp.setTitle("GFP") threshold_imp = Duplicator().run(gfp_imp) threshold_imp.setTitle("GFP_threshold_imp") ecad_imp = channel_imps[1] ecad_imp.setTitle("E-cadherin") nuc_imp = channel_imps[2] IJ.run(threshold_imp, "Make Binary", "method=Otsu background=Dark calculate") IJ.run(threshold_imp, "Fill Holes", "") erode_count = 2 for _ in range(erode_count): IJ.run(threshold_imp, "Erode", "") threshold_imp = keep_blobs_bigger_than(threshold_imp, min_size_pix=1000) threshold_imp = my_kill_borders(threshold_imp) rois = generate_cell_rois(threshold_imp) out_stats = generate_cell_shape_results(rois, gfp_imp, cal, file_name) print("Number of cells identified = {}".format(len(out_stats))) threshold_imp.changes = False threshold_imp.close() # save output save_qc_image( imp, rois, "{}_plus_overlay.tiff".format( os.path.join(output_folder, os.path.splitext(file_name)[0]))) save_cell_rois(rois, output_folder, os.path.splitext(file_name)[0]) imp.changes = False imp.close() save_output_csv(out_stats, output_folder) return out_stats
def threshold(imp, threshold): """ Threshold image """ impout = Duplicator().run(imp) IJ.setThreshold(impout, threshold, 1000000000) IJ.run(impout, "Convert to Mask", "stack") impout.setTitle("Threshold"); return impout
def ExtractChannel(imp, channel): imp_height = imp.getHeight() imp_width = imp.getWidth() channelnumber = imp.getNChannels() slicenumber = imp.getNSlices() timepoints = imp.getNFrames() ExtractedChannel = Duplicator().run(imp, channel, channel, 1, slicenumber, 1, timepoints) ExtractedChannel.setTitle("Gallery_Channel_" + str(channel)) return ExtractedChannel
def DistanceMap(_img, mpar={}, _gui=False): imp = Duplicator().run(_img) IJ.run(imp, "Exact Euclidean Distance Transform (3D)", "") imp = IJ.getImage() IJ.setMinAndMax(imp, 0, 65535) IJ.run(imp, "16-bit", "") imp.setTitle("DistanceMap") imp.hide() return (imp,)
def Smooth(_img, mpar={"width": 1}, _gui=False): imp = Duplicator().run(_img) # imp.show() print "sigma=%s" % mpar["width"] IJ.run(imp, "Gaussian Blur...", "sigma=%s stack" % mpar["width"]) # imp = IJ.getImage(); imp.setTitle("Smooth") # imp.hide() return (imp,)
def threshold(_img, threshold): imp = Duplicator().run(_img) #imp.show(); time.sleep(0.2) #IJ.setThreshold(imp, mpar['lthr'], mpar['uthr']) IJ.setThreshold(imp, threshold, 1000000000) IJ.run(imp, "Convert to Mask", "stack") imp.setTitle("Threshold"); #IJ.run(imp, "Divide...", "value=255 stack"); #IJ.setMinAndMax(imp, 0, 1); return imp
def Duplicate(imp): imp_name = imp.getTitle() imp_height = imp.getHeight() imp_width = imp.getWidth() channelnumber = imp.getNChannels() slicenumber = imp.getNSlices() timepoints = imp.getNFrames() ExtractedChannel = Duplicator().run(imp, 1, channelnumber, 1, slicenumber, 1, timepoints) ExtractedChannel.setTitle("Duplicate_" + str(imp_name)) return ExtractedChannel
def Threshold(_img, mpar={"threshold": 15}, _gui=False): print "Threshold" imp = Duplicator().run(_img) # imp.show(); time.sleep(0.2) # IJ.setThreshold(imp, mpar['lthr'], mpar['uthr']) IJ.setThreshold(imp, mpar["threshold"], 1000000000) IJ.run(imp, "Convert to Mask", "stack") imp.setTitle("Threshold") # IJ.run(imp, "Divide...", "value=255 stack"); # IJ.setMinAndMax(imp, 0, 1); return imp, _img.duplicate()
def cut(event): roi = imp.getRoi() if roi != None: newRoi = roi.clone() Dup = Duplicator().run(imp, 1, imp.getNChannels(), 1, imp.getNSlices(), 1, imp.getNFrames()) newRoi.setLocation(0,0) Dup.setRoi(newRoi) Dup.setTitle(Men.getTextField() + str(Men.getCounter())) Dup.show() #Men.setCounter() Men.addOlay(roi) imp.setOverlay(Men.getOverlay()) #setOverlay(Roi roi, java.awt.Color strokeColor, int strokeWidth, java.awt.Color fillColor) imp.getOverlay().drawLabels(True) # drawNumbers imp.deleteRoi() #make cell instance and add to position instance p.addCell(cell(p.getMainPath(), p, Men.getCounter(), Dup)) # cell(mainPath, position, ID, imp): Dup.close() Men.increaseCounter()
def cross_planes_approx_median_filter(stack_imp, filter_radius_um=5.0): """relatively computationally cheap, slightly crude approximation of median filter""" title = stack_imp.getTitle(); xy_imp = Duplicator().run(stack_imp); xy_imp.setTitle("xy {}".format(title)); xz_imp = Duplicator().run(stack_imp); xz_imp.setTitle("xz {}".format(title)); xz_imp = rot3d(xz_imp, 'x'); zy_imp = Duplicator().run(stack_imp); zy_imp.setTitle("zy {}".format(title)); zy_imp = rot3d(zy_imp, 'y'); stack_imp.changes = False; stack_imp.close(); xy_imp = stack_median_filter(xy_imp, radius_um=filter_radius_um); xz_imp = stack_median_filter(xz_imp, radius_um=filter_radius_um); zy_imp = stack_median_filter(zy_imp, radius_um=filter_radius_um); xz_imp = rot3d(xz_imp, 'x'); zy_imp = rot3d(zy_imp, '-y'); ic = ImageCalculator(); dummy = ic.run("Add create 32-bit stack", xy_imp, xz_imp); xz_imp.close(); xy_imp.close(); output_imp = ic.run("Add create 32-bit stack", dummy, zy_imp); zy_imp.close(); dummy.close(); output_imp.show(); IJ.run(output_imp, "Divide...", "value=3 stack"); output_imp.setTitle("Cross-plane median filtered {} (r={} um)".format(title, filter_radius_um).replace(".tif", "")); print("Image size after applying approx median filter = ({}x{}x{})".format(output_imp.getWidth(), output_imp.getHeight(), output_imp.getNSlices())); return output_imp;
def qc_background_regions(intensity_imp, bg_rois): """allow the user to view and correct automatically-determined background regions""" imp = Duplicator().run(intensity_imp); imp.setTitle("Background region QC"); imp.show(); imp.setPosition(1); autoset_zoom(imp); imp.setRoi(bg_rois[0]); IJ.setTool("freehand"); notOK = True; while notOK: listener = UpdateRoiImageListener(bg_rois, is_area=True); imp.addImageListener(listener); dialog = NonBlockingGenericDialog("Background region quality control"); dialog.enableYesNoCancel("Continue", "Use this region for all t"); dialog.setCancelLabel("Cancel analysis"); dialog.addMessage("Please redraw background regions as necessary...") dialog.showDialog(); if dialog.wasCanceled(): raise KeyboardInterrupt("Run canceled"); elif not(dialog.wasOKed()): this_roi = imp.getRoi(); bg_rois = [this_roi for _ in listener.getRoiList()]; imp.removeImageListener(listener); else: last_roi = imp.getRoi(); qcd_bg_rois = listener.getRoiList(); if imp.getNFrames() > imp.getNSlices(): qcd_bg_rois[imp.getT() - 1] = last_roi; else: qcd_bg_rois[imp.getZ() - 1] = last_roi; notOK = False; imp.removeImageListener(listener); imp.changes = False; imp.close(); return qcd_bg_rois;
def boxed_intensities(imp1, width, height): """Create a new image with averaged intensity regions. Parameters ---------- imp1 : ImagePlus width, height : int The width and height of the rectangles. Returns ------- imp2 : ImagePlus The resulting ImagePlus, same dimensions as imp1. """ imp2 = Duplicator().run(imp1) imp2.setTitle('heatmap-' + imp1.getTitle()) imw = imp1.getWidth() imh = imp1.getHeight() ip1 = imp1.getProcessor() ip2 = imp2.getProcessor() if (imw % width + imh % height) > 0: msg = "WARNING: image size (%dx%d) not dividable by box (%dx%d)!" IJ.log(msg % (imw, imh, width, height)) for box_y in range(0, imh / height): start_y = box_y * height for box_x in range(0, imw / width): start_x = box_x * width # print "%d %d" % (start_x, start_y) bavg = rect_avg(ip1, start_x, start_y, width, height) # print bavg rect_set(ip2, start_x, start_y, width, height, bavg) return imp2
def boxed_intensities(imp1, width, height): """Create a new image with averaged intensity regions. Parameters ---------- imp1 : ImagePlus width, height : int The width and height of the rectangles. Returns ------- imp2 : ImagePlus The resulting ImagePlus, same dimensions as imp1. """ imp2 = Duplicator().run(imp1) imp2.setTitle('heatmap-' + imp1.getTitle()) imw = imp1.getWidth() imh = imp1.getHeight() ip1 = imp1.getProcessor() ip2 = imp2.getProcessor() if (imw % width + imh % height) > 0: msg = "WARNING: image size (%dx%d) not dividable by box (%dx%d)!" log.warn(msg % (imw, imh, width, height)) for box_y in range(0, imh / height): start_y = box_y * height for box_x in range(0, imw / width): start_x = box_x * width # print "%d %d" % (start_x, start_y) bavg = rect_avg(ip1, start_x, start_y, width, height) # print bavg rect_set(ip2, start_x, start_y, width, height, bavg) return imp2
def gfp_analysis(imp, file_name, output_folder, gfp_channel_number=1, dapi_channel_number=3, red_channel_number=2, threshold_method='Otsu', do_manual_qc=False, min_size_pix=1000): """perform analysis based on gfp intensity thresholding""" try: cal = imp.getCalibration() channel_imps = ChannelSplitter.split(imp) gfp_imp = channel_imps[gfp_channel_number - 1] gfp_imp.setTitle("GFP") threshold_imp = Duplicator().run(gfp_imp) threshold_imp.setTitle("GFP_threshold_imp") ecad_imp = channel_imps[red_channel_number - 1] ecad_imp.setTitle("E-cadherin") nuc_imp = channel_imps[dapi_channel_number - 1] nuclei_locations, full_nuclei_imp, ws_seeds_imp = get_nuclei_locations( nuc_imp, cal, distance_threshold_um=10, size_threshold_um2=100) ws_seeds_imp.changes = False ws_seeds_imp.close() full_nuclei_imp.hide() IJ.run(threshold_imp, "Make Binary", "method={} background=Dark calculate".format(threshold_method)) IJ.run(threshold_imp, "Fill Holes", "") erode_count = 2 for _ in range(erode_count): IJ.run(threshold_imp, "Erode", "") for _ in range(erode_count): IJ.run(threshold_imp, "Dilate", "") threshold_imp = keep_blobs_bigger_than(threshold_imp, min_size_pix) threshold_imp = my_kill_borders(threshold_imp) rois = generate_cell_rois(threshold_imp) threshold_imp.changes = False threshold_imp.close() rois = filter_cells_by_relative_nuclear_area( rois, full_nuclei_imp, relative_nuclear_area_threshold=0.75) if do_manual_qc: rois = perform_manual_qc(imp, rois, important_channel=gfp_channel_number) no_nuclei_centroids = [ get_no_nuclei_in_cell(roi, nuclei_locations) for roi in rois ] no_enclosed_nuclei = [ get_no_nuclei_fully_enclosed(roi, full_nuclei_imp) for roi in rois ] full_nuclei_imp.changes = False full_nuclei_imp.close() out_stats = generate_cell_shape_results( rois, gfp_imp, cal, file_name, no_nuclei_centroids=no_nuclei_centroids, no_enclosed_nuclei=no_enclosed_nuclei) print("Number of cells identified = {}".format(len(out_stats))) # save output save_qc_image( imp, rois, "{}_plus_overlay.tiff".format( os.path.join(output_folder, os.path.splitext(file_name)[0]))) save_cell_rois(rois, output_folder, os.path.splitext(file_name)[0]) imp.changes = False imp.close() save_output_csv(out_stats, output_folder) except Exception as e: print("Ran into a problem analysing {}: {}. Skipping to next cell...". format(file_name, e.message)) out_stats = [] pass return out_stats
class gui(JFrame): def __init__(self): # constructor #origing of coordinates self.coordx = 10 self.coordy = 10 #inintialize values self.Canvas = None #create panel (what is inside the GUI) self.panel = self.getContentPane() self.panel.setLayout(GridLayout(9, 2)) self.setTitle('Subdividing ROIs') #define buttons here: self.Dapi_files = DefaultListModel() mylist = JList(self.Dapi_files, valueChanged=self.open_dapi_image) #mylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); mylist.setLayoutOrientation(JList.VERTICAL) mylist.setVisibleRowCount(-1) listScroller1 = JScrollPane(mylist) listScroller1.setPreferredSize(Dimension(300, 80)) self.output_files = DefaultListModel() mylist2 = JList(self.output_files) #mylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); mylist2.setLayoutOrientation(JList.VERTICAL) mylist2.setVisibleRowCount(-1) listScroller2 = JScrollPane(mylist2) listScroller2.setPreferredSize(Dimension(300, 80)) quitButton = JButton("Quit", actionPerformed=self.quit) selectInputFolderButton = JButton("Select Input", actionPerformed=self.select_input) cubifyROIButton = JButton("Cubify ROI", actionPerformed=self.cubify_ROI) saveButton = JButton("Save ROIs", actionPerformed=self.save) summsaveButton = JButton("Save Summary", actionPerformed=self.save_summary) self.textfield1 = JTextField('500') #add buttons here self.panel.add(listScroller1) self.panel.add(listScroller2) self.panel.add(selectInputFolderButton) self.panel.add(Label("Adjust the size of the ROIs")) self.panel.add(self.textfield1) self.panel.add(cubifyROIButton) self.panel.add(saveButton) self.panel.add(summsaveButton) self.panel.add(quitButton) #self.panel.add(saveButton) #self.panel.add(Zslider) #other stuff to improve the look self.pack() # packs the frame self.setVisible(True) # shows the JFrame self.setLocation(self.coordx, self.coordy) #define functions for the buttons: def quit(self, event): #quit the gui self.dispose() IJ.run("Close All") def select_input(self, event): self.input_path = IJ.getDirectory( "Select Directory containing your data") # get the files in that directory self.input_files = listdir(self.input_path) self.input_dapi = [s for s in self.input_files if "DAPI.tif" in s] self.core_names = get_core_names(self.input_dapi) # create output directory self.output_path = path.join( path.dirname(path.dirname(self.input_path)), "Cubified_ROIs") if path.isdir(self.output_path): print "Output path already created" else: mkdir(self.output_path) print "Output path created" # get the processed data self.processed_files = listdir(self.output_path) self.out_core_names = get_core_names( get_core_names(self.processed_files) ) # needs the channel and roi to be removed # populate the lists for f in set(self.core_names): if f not in set(self.out_core_names): # skip if processed self.Dapi_files.addElement(f) for f in set(self.out_core_names ): # a set so that only unique ones are shown self.output_files.addElement(f) def open_dapi_image(self, e): sender = e.getSource() IJ.run("Close All") if not e.getValueIsAdjusting(): self.name = sender.getSelectedValue() print self.name dapi_filename = path.join(self.input_path, self.name + '_DAPI.tif') #print dapi_filename if not path.exists(dapi_filename): print "I don't find the DAPI file, which is weird as I just found it before" else: #open stack and make the reslice self.dapi_image = ImagePlus(dapi_filename) #read the image # duplicate image to play with that one, and do the real processing in the background self.imp_main = Duplicator().run(self.dapi_image) self.imp_main.setTitle(self.name) self.imp_main.show() #show image on the left #self.original_image.getWindow().setLocation(self.coordx-self.original_image.getWindow().getWidth()-10,self.coordy-10) #reposition image def cubify_ROI(self, e): self.L = int(self.textfield1.text) # get info tit = self.imp_main.getTitle() self.roi = self.imp_main.getRoi() # get corners corners = get_corners(self.roi, self.L) self.corners_cleaned = clean_corners(corners, self.roi, self.L) print 'found corners' # get the overlay self.ov = overlay_corners(self.corners_cleaned, self.L) self.ov = overlay_roi(self.roi, self.ov) # write roi name self.ov = write_roi_numbers(self.ov, self.corners_cleaned, self.L) # overlay self.imp_main.setOverlay(self.ov) self.imp_main.updateAndDraw() def save(self, e): # open the other channels d1_filename = path.join(self.input_path, self.name + '_D1.tif') d2_filename = path.join(self.input_path, self.name + '_D2.tif') self.d1_image = ImagePlus(d1_filename) #read the image print "d1 image opened" self.d2_image = ImagePlus(d2_filename) #read the image print "d2 image opened" for image in [self.dapi_image, self.d1_image, self.d2_image]: print "saving rois for image " + image.getTitle() save_rois(image, self.corners_cleaned, self.L, self.output_path) print "ROIs saved" def save_summary(self, e): IJ.selectWindow(self.name) IJ.run("Flatten") imp = IJ.getImage() # downsample # scale ENLARGING or SHRINKING the canvas dimensions scale_factor = .2 new_width = str(int(imp.getWidth() * scale_factor)) new_height = str(int(imp.getHeight() * scale_factor)) IJ.selectWindow(self.name + "-1") str_to_scale = "x=" + str(scale_factor) + " y=" + str( scale_factor ) + " width=" + new_width + " height=" + new_height + " interpolation=Bilinear average create" IJ.run("Scale...", str_to_scale) # save imp2 = IJ.getImage() #IJ.saveAs("Tiff", path.join(OUTDIR, self.name + "_summaryOfROIs")) IJ.saveAsTiff( imp2, path.join(self.output_path, self.name + "_summaryOfROIs.tif")) print "summary image saved"
########## Use thresholding and selection to define UFOV ################################################################################### #IJ.run("ROI Manager...", "") # not sure if I need this IJ.setRawThreshold( original, 1, 255, '') # background pixels have value 0. See IMAGE>ADJUST>THRESHOLD IJ.run(original, "Create Selection", "") # add bounding box. See EDIT>SELECTION IJ.run(original, "To Bounding Box", "") # this box defines the UFOV. See EDIT>SELECTION IJ.resetThreshold( original) # get back original now UFOV is definedthresholding UFOV = Duplicator().run( original) # duplicate the original image, only the CFOV UFOV.setTitle("UFOV") UFOV.show() CFOV_fraction = 0.75 # choose the fraction of the UFOV that defines the CFOV IJ.run(original, "Scale... ", "x=" + str(CFOV_fraction) + " y=" + str(CFOV_fraction) + " centered") # rescale bounding box to get CFOV CFOV = Duplicator().run( original) # duplicate the original image, only the CFOV CFOV.setTitle("CFOV") CFOV.show() ######### Nema process including Re-bin image to larger pixels ################################################################################ desired_pixel_width = getPixel( ) # 6.4 mm default, remember tolerance is +/-30% current_pixel_width = CFOV.getCalibration(
######### open image using dialogue box #imp = IJ.getImage() original = IJ.openImage(getFile()) original.show() ########## Use thresholding and selection to define UFOV ################################################################################### #IJ.run("ROI Manager...", "") # not sure if I need this IJ.setRawThreshold(original, 1, 255,'') # background pixels have value 0. See IMAGE>ADJUST>THRESHOLD IJ.run(original, "Create Selection", "") # add bounding box. See EDIT>SELECTION IJ.run(original,"To Bounding Box", "") # this box defines the UFOV. See EDIT>SELECTION IJ.resetThreshold(original) # get back original now UFOV is definedthresholding UFOV = Duplicator().run(original) # duplicate the original image, only the CFOV UFOV.setTitle("UFOV") UFOV.show() CFOV_fraction = 0.75 # choose the fraction of the UFOV that defines the CFOV IJ.run(original,"Scale... ", "x="+str(CFOV_fraction)+" y="+str(CFOV_fraction)+" centered") # rescale bounding box to get CFOV CFOV = Duplicator().run(original) # duplicate the original image, only the CFOV CFOV.setTitle("CFOV") CFOV.show() ######### Nema process including Re-bin image to larger pixels ################################################################################ desired_pixel_width = getPixel() # 6.4 mm default, remember tolerance is +/-30% current_pixel_width = CFOV.getCalibration().pixelWidth #get pixel width, 1.16 mm shrink_factor = int(desired_pixel_width/current_pixel_width) # must be an integer
def ecad_analysis(imp, file_name, output_folder, gfp_channel_number=1, dapi_channel_number=3, red_channel_number=2, do_manual_qc=False): """perform analysis based on marker-driven watershed of junction labelling image""" cal = imp.getCalibration() channel_imps = ChannelSplitter.split(imp) ecad_imp = channel_imps[red_channel_number - 1] ecad_imp.setTitle("E-cadherin") if gfp_channel_number is not None: gfp_imp = channel_imps[gfp_channel_number - 1] gfp_imp.setTitle("GFP") else: gfp_imp = Duplicator().run(ecad_imp) gfp_imp.setTitle("GFP") threshold_imp = Duplicator().run(ecad_imp) threshold_imp.setTitle("Ecad_threshold_imp") nuc_imp = channel_imps[dapi_channel_number - 1] nuclei_locations, full_nuclei_imp, ws_seed_imp = get_nuclei_locations( nuc_imp, cal, distance_threshold_um=10, size_threshold_um2=100) full_nuclei_imp.hide() binary_cells_imp = generate_cell_masks(ws_seed_imp, ecad_imp, find_edges=True) ws_seed_imp.changes = False ws_seed_imp.close() rois = generate_cell_rois(binary_cells_imp) binary_cells_imp.changes = False binary_cells_imp.close() if do_manual_qc: print("gfp_channel_number= {}".format(gfp_channel_number)) print("red_channel_number = {}".format(red_channel_number)) manual_qc_rois = perform_manual_qc( imp, rois, important_channel=red_channel_number) if manual_qc_rois is not None: rois = manual_qc_rois no_nuclei_centroids = [ get_no_nuclei_in_cell(roi, nuclei_locations) for roi in rois ] no_enclosed_nuclei = [ get_no_nuclei_fully_enclosed(roi, full_nuclei_imp) for roi in rois ] full_nuclei_imp.changes = False full_nuclei_imp.close() out_stats = generate_cell_shape_results( rois, gfp_imp, cal, file_name, no_nuclei_centroids=no_nuclei_centroids, no_enclosed_nuclei=no_enclosed_nuclei) if gfp_channel_number is None: for idx, cell_shape_result in enumerate(out_stats): cell_shape_result.cell_gfp_I_mean = 0 cell_shape_result.cell_gfp_I_sd = 0 out_stats[idx] = cell_shape_result print("Number of cells identified = {}".format(len(out_stats))) # save output save_qc_image( imp, rois, "{}_plus_overlay.tiff".format( os.path.join(output_folder, os.path.splitext(file_name)[0]))) save_cell_rois(rois, output_folder, os.path.splitext(file_name)[0]) imp.changes = False imp.close() save_output_csv(out_stats, output_folder) return out_stats