def suppfig_metrics(test_root, save_root, save_figure=False): """ cyto performance measured with AJI and boundary precision """ ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [] aji = [] model_type = 'cyto' masks.append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) #masks.append(np.load(os.path.join(save_root, 'unet3_residual_on_style_on_concatenation_off_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks)): aji.append(metrics.aggregated_jaccard_index(test_labels, masks[j])) fsc.append(metrics.aggregated_jaccard_index(test_labels, masks[j])) for m,model_type in enumerate(model_types): masks[m].append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) #masks[m].append(np.load(os.path.join(save_root, 'unet3_residual_on_style_on_concatenation_off_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'unet2_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks[m])): aps[m].append(metrics.average_precision(test_labels, masks[m][j], threshold=thresholds)[0])
def test_unets(model_root, test_root, save_root, model_type='cyto'): """ test trained unets """ device = mx.gpu() ntest = len(glob(os.path.join(test_root, '*_img.tif'))) if model_type[:4] == 'cyto': channels = [2, 1] else: channels = [0, 0] concatenation = [1, 1, 0] residual_on = [0, 0, 1] style_on = [0, 0, 1] nclasses = [3, 2, 3] sstr = ['off', 'on'] aps = np.zeros((len(concatenation), ntest, len(thresholds))) test_data = [ io.imread(os.path.join(test_root, '%03d_img.tif' % i)) for i in range(ntest) ] test_labels = [ io.imread(os.path.join(test_root, '%03d_masks.tif' % i)) for i in range(ntest) ] if model_type != 'cyto_sp': dat = np.load(os.path.join(test_root, 'predicted_diams.npy'), allow_pickle=True).item() if model_type == 'cyto': rescale = 30. / dat['predicted_diams'] else: rescale = 17. / dat['predicted_diams'] else: rescale = np.ones(len(test_data)) for k in range(1): #len(concatenation)): pretrained_models = get_pretrained_models(model_root, 1, nclasses[k], residual_on[k], style_on[k], concatenation[k]) print(pretrained_models) model = models.UnetModel(device=device, pretrained_model=pretrained_models) masks = model.eval(test_data, channels=channels, rescale=rescale, net_avg=True)[0] ap = metrics.average_precision(test_labels, masks, threshold=thresholds)[0] print(ap[:, [0, 5, 8]].mean(axis=0)) aps[k] = ap np.save( os.path.join( save_root, 'unet%d_residual_%s_style_%s_concatenation_%s_%s_masks.npy' % (nclasses[k], sstr[residual_on[k]], sstr[style_on[k]], sstr[concatenation[k]], model_type)), masks)
def train_unets(data_root): """ train unets with 3 or 2 classes and different architectures (12 networks total) """ # can also run on command line for GPU cluster # python -m cellpose --train --use_gpu --dir images_cyto/train/ --test_dir images_cyto/test/ --img_filter _img --pretrained_model None --chan 2 --chan2 1 --unet "$1" --nclasses "$2" --learning_rate "$3" --residual_on "$4" --style_on "$5" --concatenation "$6" device = mx.gpu() ntest = len(glob(os.path.join(data_root, 'test/*_img.tif'))) ntrain = len(glob(os.path.join(data_root, 'train/*_img.tif'))) channels = [2, 1] concatenation = [1, 1, 0] residual_on = [0, 0, 1] style_on = [0, 0, 1] nclasses = [3, 2, 3] # load images train_root = os.path.join(data_root, 'train/') train_data = [ io.imread(os.path.join(train_root, '%03d_img.tif' % i)) for i in range(ntrain) ] train_labels = [ io.imread(os.path.join(train_root, '%03d_masks.tif' % i)) for i in range(ntrain) ] test_root = os.path.join(data_root, 'test/') test_data = [ io.imread(os.path.join(test_root, '%03d_img.tif' % i)) for i in range(ntest) ] test_labels = [ io.imread(os.path.join(test_root, '%03d_masks.tif' % i)) for i in range(ntest) ] # train networks for k in range(len(concatenation)): # 4 nets for each for l in range(4): model = models.UnetModel(device=device, pretrained_model=None, diam_mean=30, residual_on=residual_on[k], style_on=style_on[k], concatenation=concatenation[k], nclasses=nclasses[k]) model.train(train_data, train_labels, test_data, test_labels, channels=channels, rescale=True, save_path=train_root)
def test_cellpose_kfold_aug(data_root, save_root): """ test trained cellpose networks on all cyto images """ device = mx.gpu() ntest = 68 concatenation = [0] residual_on = [1] style_on = [1] channels = [2, 1] aps = np.zeros((9, 68, len(thresholds))) for j in range(9): train_root = os.path.join(data_root, 'train%d/' % j) model_root = os.path.join(train_root, 'models/') test_root = os.path.join(data_root, 'test%d/' % j) test_data = [ io.imread(os.path.join(test_root, '%03d_img.tif' % i)) for i in range(ntest) ] test_labels = [ io.imread(os.path.join(test_root, '%03d_masks.tif' % i)) for i in range(ntest) ] k = 0 pretrained_models = get_pretrained_models(model_root, 0, 3, residual_on[k], style_on[k], concatenation[k]) print(pretrained_models) cp_model = models.CellposeModel(device=device, pretrained_model=pretrained_models) dat = np.load(test_root + 'predicted_diams.npy', allow_pickle=True).item() rescale = 30. / dat['predicted_diams'] masks = cp_model.eval(test_data, channels=channels, rescale=rescale, net_avg=True, augment=True)[0] ap = metrics.average_precision(test_labels, masks, threshold=thresholds)[0] print(ap[:, [0, 5, 8]].mean(axis=0)) aps[j] = ap return aps
def compare_masks(data_dir, image_names, runtype, model_type): """ Helper function to check if outputs given by a test are exactly the same as the ground truth outputs. """ data_dir_2D = data_dir.joinpath('2D') data_dir_3D = data_dir.joinpath('3D') for image_name in image_names: check = False if '2D' in runtype and '2D' in image_name: image_file = str(data_dir_2D.joinpath(image_name)) name = os.path.splitext(image_file)[0] output_test = name + '_cp_masks.png' output_true = name + '_%s_masks.png' % model_type check = True elif '3D' in runtype and '3D' in image_name: image_file = str(data_dir_3D.joinpath(image_name)) name = os.path.splitext(image_file)[0] output_test = name + '_cp_masks.tif' output_true = name + '_%s_masks.tif' % model_type check = True if check: if os.path.exists(output_test): print('checking output %s' % output_test) masks_test = io.imread(output_test) masks_true = io.imread(output_true) ap = metrics.average_precision(masks_true, masks_test)[0] print('average precision of [%0.3f %0.3f %0.3f]' % (ap[0], ap[1], ap[2])) ap_precision = np.allclose(ap, np.ones(3), rtol=r_tol, atol=a_tol) matching_pix = np.logical_and(masks_test > 0, masks_true > 0).mean() all_pix = (masks_test > 0).mean() pix_precision = np.allclose(all_pix, matching_pix, rtol=r_tol, atol=a_tol) assert all([ap_precision, pix_precision]) else: print('ERROR: no output file of name %s found' % output_test) assert False
def LoadMultiple(self, event): wcd = 'Images (*.jpg,*.png,*.bmp,*.tif)|*.jpg;*.png;*.bmp;*.tif' dlg = wx.FileDialog(self, "Load files", "", "", wcd, wx.FD_MULTIPLE) if dlg.ShowModal() == wx.ID_OK: print('OK loaded') files = dlg.GetPaths() print(files) print('Number of files: ', len(files)) imgs = [io.imread(f) for f in files] type = os.path.splitext(files[0])[1] self.channels = [self.channel, 0] modeltype = ('cyto', 'nuclei')[self.model] model = models.Cellpose(gpu=False, model_type=modeltype) masks, flows, styles, diams = model.eval( imgs, diameter=None, flow_threshold=self.number1, cellprob_threshold=self.number3, channels=self.channels) io.masks_flows_to_seg(imgs, masks, flows, diams, files, self.channels) for f in files: npy = f.replace(type, '_seg.npy') data, maxkey = get_labels(npy) dir = npy.replace('_seg.npy', '_map.csv') np.savetxt(dir, data, delimiter=",") if self.centerShow: centers = np.asarray(get_centers(data, maxkey)) dir = npy.replace('_seg.npy', '_center.csv') np.savetxt(dir, centers, delimiter=",") os.remove(npy)
def test_timing(test_root, save_root): itest = 14 test_data = io.imread(os.path.join(test_root, '%03d_img.tif' % itest)) dat = np.load(os.path.join(test_root, 'predicted_diams.npy'), allow_pickle=True).item() rescale = 30. / dat['predicted_diams'][itest] Ly, Lx = test_data.shape[1:] test_data = cv2.resize(np.transpose(test_data, (1, 2, 0)), (int(Lx * rescale), int(Ly * rescale))) devices = [mx.gpu(), mx.cpu()] bsize = [256, 512, 1024] t100 = np.zeros((2, 3, 2)) for d, device in enumerate(devices): model = models.CellposeModel(device=device, pretrained_model=None) for j in range(3): if j == 2: test_data = np.tile(test_data, (2, 2, 1)) img = test_data[:bsize[j], :bsize[j]] imgs = [img for i in range(100)] for k in [0, 1]: tic = time.time() masks = model.eval(imgs, channels=[2, 1], rescale=1.0, net_avg=k)[0] print(masks[0].max()) t100[d, j, k] = time.time() - tic print(t100[d, j, k])
def main(argv): parser = ParserCreator.createArgumentParser("./predict.yml") if len(argv) == 1: parser.print_help(sys.stderr) sys.exit(1) args = parser.parse_args(argv[1:]) print(args) model = models.Cellpose(gpu=args.gpu, model_type=args.modelType) files = [ os.path.join(args.dataPath, image) for image in os.listdir(args.dataPath) if (image.lower().endswith(".tif") or image.lower().endswith(".jpg") or image.lower().endswith(".png")) and not image.lower().endswith("_cp_masks.png") ] channels = [[args.segChannel, args.nucleiChannel]] * len(files) diameter = args.diameter if diameter == 0: diameter = None for channel, filename in zip(channels, files): img = io.imread(filename) masks, flows, styles, diams = model.eval(img, diameter=diameter, channels=channel) newFilename = filename.split(".")[0] + "_c" + str( args.segChannel) + "." + filename.split(".")[1] io.save_to_png(img, masks, flows, newFilename)
def test_cyto2_to_seg(data_dir, image_names): clear_output(data_dir, image_names) image_names = ['rgb_2D.png', 'rgb_2D_tif.tif'] file_names = [str(data_dir.joinpath('2D').joinpath(image_name)) for image_name in image_names] imgs = [io.imread(file_name) for file_name in file_names] model_type = 'cyto2' model = models.Cellpose(model_type=model_type) channels = [2,1] masks, flows, styles, diams = model.eval(imgs, diameter=30, channels=channels, net_avg=False) io.masks_flows_to_seg(imgs, masks, flows, diams, file_names)
def test_class_3D(data_dir, image_names): clear_output(data_dir, image_names) img = io.imread(str(data_dir.joinpath('3D').joinpath('rgb_3D.tif'))) model_types = ['nuclei'] chan = [1] chan2 = [0] for m,model_type in enumerate(model_types): model = models.Cellpose(model_type='nuclei') masks = model.eval(img, do_3D=True, diameter=25, channels=[chan[m],chan2[m]], net_avg=False)[0] io.imsave(str(data_dir.joinpath('3D').joinpath('rgb_3D_cp_masks.tif')), masks) compare_masks(data_dir, ['rgb_3D.tif'], '3D', model_type) clear_output(data_dir, image_names)
def size_distributions(data_root, save_root): """ size distributions for all images """ ntest = 68 sz_dist = np.zeros((9, ntest)) for j in range(9): test_root = os.path.join(data_root, 'test%d/' % j) test_labels = [ io.imread(os.path.join(test_root, '%03d_masks.tif' % i)) for i in range(ntest) ] sz_dist[j] = np.array( [utils.size_distribution(lbl) for lbl in test_labels]) np.save(os.path.join(save_root, 'size_distribution.npy'), sz_dist) return sz_dist
def test_class_2D(data_dir, image_names): clear_output(data_dir, image_names) img = io.imread(str(data_dir.joinpath('2D').joinpath('rgb_2D.png'))) model_types = ['nuclei'] chan = [1] chan2 = [0] for m,model_type in enumerate(model_types): model = models.Cellpose(model_type=model_type) masks, flows, _, _ = model.eval(img, diameter=0, channels=[chan[m],chan2[m]], net_avg=False) io.imsave(str(data_dir.joinpath('2D').joinpath('rgb_2D_cp_masks.png')), masks) compare_masks(data_dir, ['rgb_2D.png'], '2D', model_type) clear_output(data_dir, image_names) if MATPLOTLIB: fig = plt.figure(figsize=(8,3)) plot.show_segmentation(fig, img, masks, flows[0], channels=[chan[m],chan2[m]])
def ClickFind(self, event): #segmentation modeltype = ('cyto', 'nuclei')[self.model] print(modeltype) model = models.Cellpose(gpu=False, model_type=modeltype) self.channels = [self.channel, 0] if self.regionShow: image = self.pilImg2 pathname = self.pilImg1fullpath.replace(self.pilImg1type, '_cropped.tif') image.save(pathname) self.nppilImg1 = io.imread(pathname) else: self.nppilImg1 = io.imread(self.pilImg1fullpath) print(self.number1, self.number3) self.masks, self.flows, styles, self.diams = model.eval( self.nppilImg1, diameter=None, flow_threshold=self.number1, cellprob_threshold=self.number3, channels=self.channels) io.masks_flows_to_seg(self.nppilImg1, self.masks, self.flows, self.diams, self.pilImg1fullpath, self.channels) dir = self.pilImg1fullpath.replace(self.pilImg1type, '_seg.npy') self.data, self.maxkey = get_labels(dir, region=None) self.m_height.SetLabel( str(self.maxkey) + ' (avg. Diameter in pixels: ' + str(round(self.diams)) + ')') os.remove(dir) if self.centerShow: self.centers = get_centers(self.data, self.maxkey) newbox = wx.MessageDialog(None, 'Results succesfully saved', 'Notice', wx.OK) answer = newbox.ShowModal() newbox.Destroy()
def test_cellpose(test_root, save_root, pretrained_models, diam_file=None, model_type='cyto'): """ test single cellpose net or 4 nets averaged """ device = mx.gpu() ntest = len(glob(os.path.join(test_root, '*_img.tif'))) if model_type[:4] != 'nuclei': channels = [2, 1] else: channels = [0, 0] test_data = [ io.imread(os.path.join(test_root, '%03d_img.tif' % i)) for i in range(ntest) ] # saved diameters if model_type != 'cyto_sp': if diam_file is None: dat = np.load(os.path.join(test_root, 'predicted_diams.npy'), allow_pickle=True).item() else: dat = np.load(diam_file, allow_pickle=True).item() if model_type == 'cyto': rescale = 30. / dat['predicted_diams'] else: rescale = 17. / dat['predicted_diams'] else: rescale = np.ones(len(test_data)) model = models.CellposeModel(device=device, pretrained_model=pretrained_models) masks = model.eval(test_data, channels=channels, rescale=rescale)[0] np.save(os.path.join(save_root, 'cellpose_%s_masks.npy' % model_type), masks)
def segment_obj_indiv(path_scan, obj_name, str_channel, img_ext, new_size, model_type, diameter, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): """ Will recursively search folder for images to be analyzed! Parameters ---------- path_scan : [type] [description] str_nuclei : [type] [description] img_ext : [type] [description] diameter : [type] [description] model_type : [type] [description] path_save : pathlib object or string Path to save results, - If Pathlib object, then this absolute path is used. - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). input_subfolder : str Name of subfolder that contains results. If specified ONLY files in this folder will be processed. callback_log : [type], optional [description], by default None callback_status : [type], optional [description], by default None callback_progress : [type], optional [description], by default None Returns ------- [type] [description] """ # Print all input parameters par_dict = locals() par_dict = clean_par_dict(par_dict) log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) # Configurations config = { 'model_type': model_type, 'diameter': diameter, 'net_avg': net_avg, 'resample': resample } channels = [0, 1] # Use provided absolute user-path to save images. if isinstance(path_save, pathlib.PurePath): path_save_results = path_save if not path_save_results.is_dir(): path_save_results.mkdir(parents=True) else: path_save_str_replace = path_save if not path_scan.is_dir(): log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) return # Search for file to be analyzed log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) files_proc = [] for path_img in path_scan.rglob(f'*{str_channel}*{img_ext}'): if input_subfolder: if path_img.parts[-2] == input_subfolder: files_proc.append(path_img) else: files_proc.append(path_img) n_imgs = len(files_proc) if n_imgs == 0: log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) return # Process files for idx, path_img in enumerate(files_proc): imgs = [] files = [] sizes_orginal = [] log_message(f'Segmenting image : {path_img.name}', callback_fun=callback_log) if callback_status: callback_status(f'Segmenting image : {path_img.name}') if callback_progress: progress = float((idx + 1) / n_imgs) callback_progress(progress) # Read images img = io.imread(str(path_img)) if img.ndim != 2: log_message( f'\nERROR\n Input image has to be 2D. Current image is {img.ndim}D', callback_fun=callback_log) continue # IMPORTANT: CV2 resize is defined as (width, height) print('>>> process file') print(f'input file (size): {img.shape}') sizes_orginal.append(img.shape) # Resize if new_size: # New size can also be defined as a scalar factor if len(new_size) == 1: scale_factor = new_size[0] img_size = img.shape new_size = tuple(int(ti / scale_factor) for ti in img_size) # IMPORTANT: CV2 resize is defined as (width, height) dsize = (new_size[1], new_size[0]) img = cv2.resize(img, dsize) print(f'resized file (size): {img.shape}') # For object segmentation img_zeros = np.zeros(img.shape) img_3d_dpi = np.dstack([img_zeros, img_zeros, img]) imgs.append(img_3d_dpi) files.append(path_img) # >>> Call function for prediction data = { 'imgs': imgs, 'file_names': files, 'channels': channels, 'obj_name': obj_name, 'sizes_orginal': sizes_orginal, 'new_size': new_size } # Create new output path if specified if not isinstance(path_save, pathlib.PurePath): path_save_results = create_output_path(path_img.parent, path_save_str_replace, subfolder='', create_path=True) path_save_settings = path_save_results cellpose_predict(data, config, path_save=path_save_results, callback_log=callback_log) # Save settings if len(imgs) > 0: fp = open( str(path_save_results / f'segmentation_settings__{obj_name}.json'), "w") json.dump(par_dict, fp, indent=4, sort_keys=True) fp.close() log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log)
def getVoronoiStyle(seg_file,max_voro_area,voro_imfile,voro_imfile_2,voro_outfile,voro_transfile): temp = np.asarray(np.load(seg_file,allow_pickle=True)).item() masks = temp['masks'] im = np.zeros_like(np.array(masks)) fro = pd.DataFrame(measure.regionprops_table(masks, properties=['label','centroid'])) points_mask = np.array(fro[['centroid-0','centroid-1']].to_numpy()) vor = Voronoi(points_mask) my_dpi=im.shape[1] plt.rcParams['figure.dpi'] = my_dpi plt.rcParams['figure.figsize'] = ( im.shape[0]/my_dpi,im.shape[1]/my_dpi) fig = plt.figure(); for simplex in vor.ridge_vertices: simplex = np.asarray(simplex) if np.all(simplex >= 0): plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',c='black',linewidth=.2) center = points_mask.mean(axis=0) for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices): simplex = np.asarray(simplex) if np.any(simplex < 0): i = simplex[simplex >= 0][0] # finite end Voronoi vertex t = points_mask[pointidx[0]] - points_mask[pointidx[1]] # tangent t = t / np.linalg.norm(t) n = np.array([-t[1], t[0]]) # normal midpoint = points_mask[pointidx].mean(axis=0) far_point = vor.vertices[i] + np.sign(np.dot(midpoint - center, n)) * n * 100 plt.plot([vor.vertices[i,0], far_point[0]], [vor.vertices[i,1], far_point[1]], 'k-',c='black',linewidth=.2) plt.xlim([0, im.shape[0]]); plt.ylim([0,im.shape[1]]) plt.axis('off') fig.tight_layout(pad=0) plt.savefig(voro_imfile, dpi=my_dpi, #bbox_inches='tight',#dpi=my_dpi, transparent=False, pad_inches=0,facecolor='white') plt.close() im2 = io.imread(voro_imfile) voro = (im2[:,:,0]) voro = voro[1:-1, 1:-1] voro = np.pad(voro, pad_width=1, mode='constant') distance = ndi.distance_transform_edt(voro) coords = peak_local_max(distance, footprint=np.ones((1, 1)), labels=voro) mask = np.zeros(distance.shape, dtype=bool) mask[tuple(coords.T)] = True markers, _ = ndi.label(mask) labels = segmentation.watershed(-distance, markers, mask=voro) labels = morphology.remove_small_objects(labels, min_size=40, connectivity=1, in_place=False) labels = morphology.dilation(labels, morphology.square(3)) segmasks = masks segmasks = morphology.dilation(segmasks,morphology.square(3)) sizeOfSegs = pd.DataFrame(measure.regionprops_table(labels, properties=['label','area'])) bigMasks = np.array(sizeOfSegs[sizeOfSegs['area']>=max_voro_area]['label']) newVorMask = np.copy(labels)[::-1,:] for bMI in range(len(bigMasks)): print("progress:"+str(bMI)+'/'+str(len(bigMasks))) chckMtx = (labels == bigMasks[bMI])[::-1,:] for i in range(len(points_mask)): confirm = points_mask[i] print(points_mask[i]) print("---") tmp_cellpose_mask = (morphology.dilation((segmasks == int(fro[(fro['centroid-0']==confirm[0])&(fro['centroid-1']==confirm[1])]['label'])).T,morphology.disk(11))).astype(int) tmp_voronoi_mask = 2*chckMtx.astype(int) tmp_join = segmentation.join_segmentations(tmp_cellpose_mask,tmp_voronoi_mask) tmp_join = (tmp_join == np.max(tmp_join)) newVorMask[newVorMask == bigMasks[bMI]] = 0 newVorMask[tmp_join] = bigMasks[bMI] np.save(voro_outfile, newVorMask.T, allow_pickle=True, fix_imports=True) io.imsave(voro_imfile_2, segmentation.find_boundaries(newVorMask).T) oldAssign = pd.DataFrame(measure.regionprops_table(masks, properties=['label','centroid'])) newAssign = pd.DataFrame(measure.regionprops_table(newVorMask, properties=['label','centroid'])) Clps2Voro = pd.DataFrame() for nlab in range(newAssign.shape[0]): tmpMtx = (newVorMask == newAssign['label'][nlab]) for olab in range(oldAssign.shape[0]): if (tmpMtx[int(np.round(oldAssign['centroid-1'][olab])),int(np.round(oldAssign['centroid-0'][olab]))]): Clps2Voro = Clps2Voro.append(pd.DataFrame([newAssign['label'][nlab], oldAssign['label'][olab]]).T) Clps2Voro = Clps2Voro.rename(columns={0: "voro_label", 1: "clps_label"}) Clps2Voro = Clps2Voro.reset_index(drop=True) Clps2Voro.to_csv(voro_transfile)
def main(): parser = argparse.ArgumentParser(description='cellpose parameters') parser.add_argument('--check_mkl', action='store_true', help='check if mkl working') parser.add_argument( '--mkldnn', action='store_true', help='for mxnet, force MXNET_SUBGRAPH_BACKEND = "MKLDNN"') parser.add_argument('--train', action='store_true', help='train network using images in dir') parser.add_argument('--dir', required=False, default=[], type=str, help='folder containing data to run or train on') parser.add_argument('--look_one_level_down', action='store_true', help='') parser.add_argument('--mxnet', action='store_true', help='use mxnet') parser.add_argument('--img_filter', required=False, default=[], type=str, help='end string for images to run on') parser.add_argument('--use_gpu', action='store_true', help='use gpu if mxnet with cuda installed') parser.add_argument( '--fast_mode', action='store_true', help="make code run faster by turning off 4 network averaging") parser.add_argument( '--resample', action='store_true', help= "run dynamics on full image (slower for images with large diameters)") parser.add_argument( '--no_interp', action='store_true', help='do not interpolate when running dynamics (was default)') # settings for running cellpose parser.add_argument( '--do_3D', action='store_true', help='process images as 3D stacks of images (nplanes x nchan x Ly x Lx' ) parser.add_argument('--pretrained_model', required=False, default='cyto', type=str, help='model to use') parser.add_argument( '--chan', required=False, default=0, type=int, help='channel to segment; 0: GRAY, 1: RED, 2: GREEN, 3: BLUE') parser.add_argument( '--chan2', required=False, default=0, type=int, help= 'nuclear channel (if cyto, optional); 0: NONE, 1: RED, 2: GREEN, 3: BLUE' ) parser.add_argument('--invert', required=False, action='store_true', help='invert grayscale channel') parser.add_argument( '--all_channels', action='store_true', help= 'use all channels in image if using own model and images with special channels' ) parser.add_argument( '--diameter', required=False, default=30., type=float, help='cell diameter, if 0 cellpose will estimate for each image') parser.add_argument( '--stitch_threshold', required=False, default=0.0, type=float, help= 'compute masks in 2D then stitch together masks with IoU>0.9 across planes' ) parser.add_argument( '--flow_threshold', required=False, default=0.4, type=float, help='flow error threshold, 0 turns off this optional QC step') parser.add_argument('--cellprob_threshold', required=False, default=0.0, type=float, help='cell probability threshold, centered at 0.0') parser.add_argument('--save_png', action='store_true', help='save masks as png') parser.add_argument('--save_outlines', action='store_true', help='save outlines as text file for ImageJ') parser.add_argument('--save_tif', action='store_true', help='save masks as tif') parser.add_argument('--no_npy', action='store_true', help='suppress saving of npy') parser.add_argument( '--channel_axis', required=False, default=None, type=int, help='axis of image which corresponds to image channels') parser.add_argument('--z_axis', required=False, default=None, type=int, help='axis of image which corresponds to Z dimension') parser.add_argument('--exclude_on_edges', action='store_true', help='discard masks which touch edges of image') parser.add_argument( '--unet', required=False, default=0, type=int, help='run standard unet instead of cellpose flow output') parser.add_argument( '--nclasses', required=False, default=3, type=int, help='if running unet, choose 2 or 3, otherwise not used') # settings for training parser.add_argument('--train_size', action='store_true', help='train size network at end of training') parser.add_argument('--mask_filter', required=False, default='_masks', type=str, help='end string for masks to run on') parser.add_argument('--test_dir', required=False, default=[], type=str, help='folder containing test data (optional)') parser.add_argument('--learning_rate', required=False, default=0.2, type=float, help='learning rate') parser.add_argument('--n_epochs', required=False, default=500, type=int, help='number of epochs') parser.add_argument('--batch_size', required=False, default=8, type=int, help='batch size') parser.add_argument('--residual_on', required=False, default=1, type=int, help='use residual connections') parser.add_argument('--style_on', required=False, default=1, type=int, help='use style vector') parser.add_argument( '--concatenation', required=False, default=0, type=int, help= 'concatenate downsampled layers with upsampled layers (off by default which means they are added)' ) args = parser.parse_args() if args.check_mkl: mkl_enabled = models.check_mkl((not args.mxnet)) else: mkl_enabled = True if not args.train and (mkl_enabled and args.mkldnn): os.environ["MXNET_SUBGRAPH_BACKEND"] = "MKLDNN" else: os.environ["MXNET_SUBGRAPH_BACKEND"] = "" if len(args.dir) == 0: if not GUI_ENABLED: logger.critical('ERROR: %s' % GUI_ERROR) if GUI_IMPORT: logger.critical( 'GUI FAILED: GUI dependencies may not be installed, to install, run' ) logger.critical(' pip install cellpose[gui]') else: gui.run() else: use_gpu = False channels = [args.chan, args.chan2] # find images if len(args.img_filter) > 0: imf = args.img_filter else: imf = None device, gpu = models.assign_device((not args.mxnet), args.use_gpu) if not args.train and not args.train_size: tic = time.time() if not (args.pretrained_model == 'cyto' or args.pretrained_model == 'nuclei' or args.pretrained_model == 'cyto2'): cpmodel_path = args.pretrained_model if not os.path.exists(cpmodel_path): logger.warning( 'model path does not exist, using cyto model') args.pretrained_model = 'cyto' image_names = io.get_image_files( args.dir, args.mask_filter, imf=imf, look_one_level_down=args.look_one_level_down) nimg = len(image_names) cstr0 = ['GRAY', 'RED', 'GREEN', 'BLUE'] cstr1 = ['NONE', 'RED', 'GREEN', 'BLUE'] logger.info( '>>>> running cellpose on %d images using chan_to_seg %s and chan (opt) %s' % (nimg, cstr0[channels[0]], cstr1[channels[1]])) if args.pretrained_model == 'cyto' or args.pretrained_model == 'nuclei' or args.pretrained_model == 'cyto2': if args.mxnet and args.pretrained_model == 'cyto2': logger.warning( 'cyto2 model not available in mxnet, using cyto model') args.pretrained_model = 'cyto' model = models.Cellpose(gpu=gpu, device=device, model_type=args.pretrained_model, torch=(not args.mxnet)) else: if args.all_channels: channels = None model = models.CellposeModel(gpu=gpu, device=device, pretrained_model=cpmodel_path, torch=(not args.mxnet)) if args.diameter == 0: if args.pretrained_model == 'cyto' or args.pretrained_model == 'nuclei' or args.pretrained_model == 'cyto2': diameter = None logger.info('>>>> estimating diameter for each image') else: logger.info( '>>>> using user-specified model, no auto-diameter estimation available' ) diameter = model.diam_mean else: diameter = args.diameter logger.info('>>>> using diameter %0.2f for all images' % diameter) tqdm_out = utils.TqdmToLogger(logger, level=logging.INFO) for image_name in tqdm(image_names, file=tqdm_out): image = io.imread(image_name) out = model.eval(image, channels=channels, diameter=diameter, do_3D=args.do_3D, net_avg=(not args.fast_mode), augment=False, resample=args.resample, flow_threshold=args.flow_threshold, cellprob_threshold=args.cellprob_threshold, invert=args.invert, batch_size=args.batch_size, interp=(not args.no_interp), channel_axis=args.channel_axis, z_axis=args.z_axis) masks, flows = out[:2] if len(out) > 3: diams = out[-1] else: diams = diameter if args.exclude_on_edges: masks = utils.remove_edge_masks(masks) if not args.no_npy: io.masks_flows_to_seg(image, masks, flows, diams, image_name, channels) if args.save_png or args.save_tif or args.save_outlines: io.save_masks(image, masks, flows, image_name, png=args.save_png, tif=args.save_tif, outlines=args.save_outlines) logger.info('>>>> completed in %0.3f sec' % (time.time() - tic)) else: if args.pretrained_model == 'cyto' or args.pretrained_model == 'nuclei' or args.pretrained_model == 'cyto2': if args.mxnet and args.pretrained_model == 'cyto2': logger.warning( 'cyto2 model not available in mxnet, using cyto model') args.pretrained_model = 'cyto' cpmodel_path = models.model_path(args.pretrained_model, 0, not args.mxnet) if args.pretrained_model == 'cyto': szmean = 30. else: szmean = 17. else: cpmodel_path = os.fspath(args.pretrained_model) szmean = 30. test_dir = None if len(args.test_dir) == 0 else args.test_dir output = io.load_train_test_data(args.dir, test_dir, imf, args.mask_filter, args.unet, args.look_one_level_down) images, labels, image_names, test_images, test_labels, image_names_test = output # training with all channels if args.all_channels: img = images[0] if img.ndim == 3: nchan = min(img.shape) elif img.ndim == 2: nchan = 1 channels = None else: nchan = 2 # model path if not os.path.exists(cpmodel_path): if not args.train: error_message = 'ERROR: model path missing or incorrect - cannot train size model' logger.critical(error_message) raise ValueError(error_message) cpmodel_path = False logger.info('>>>> training from scratch') if args.diameter == 0: rescale = False logger.info( '>>>> median diameter set to 0 => no rescaling during training' ) else: rescale = True szmean = args.diameter else: rescale = True args.diameter = szmean logger.info('>>>> pretrained model %s is being used' % cpmodel_path) args.residual_on = 1 args.style_on = 1 args.concatenation = 0 if rescale and args.train: logger.info( '>>>> during training rescaling images to fixed diameter of %0.1f pixels' % args.diameter) # initialize model if args.unet: model = core.UnetModel(device=device, pretrained_model=cpmodel_path, diam_mean=szmean, residual_on=args.residual_on, style_on=args.style_on, concatenation=args.concatenation, nclasses=args.nclasses, nchan=nchan) else: model = models.CellposeModel(device=device, torch=(not args.mxnet), pretrained_model=cpmodel_path, diam_mean=szmean, residual_on=args.residual_on, style_on=args.style_on, concatenation=args.concatenation, nchan=nchan) # train segmentation model if args.train: cpmodel_path = model.train(images, labels, train_files=image_names, test_data=test_images, test_labels=test_labels, test_files=image_names_test, learning_rate=args.learning_rate, channels=channels, save_path=os.path.realpath( args.dir), rescale=rescale, n_epochs=args.n_epochs, batch_size=args.batch_size) model.pretrained_model = cpmodel_path logger.info('>>>> model trained and saved to %s' % cpmodel_path) # train size model if args.train_size: sz_model = models.SizeModel(cp_model=model, device=device) sz_model.train(images, labels, test_images, test_labels, channels=channels, batch_size=args.batch_size) if test_images is not None: predicted_diams, diams_style = sz_model.eval( test_images, channels=channels) if test_labels[0].ndim > 2: tlabels = [lbl[0] for lbl in test_labels] else: tlabels = test_labels ccs = np.corrcoef( diams_style, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] cc = np.corrcoef( predicted_diams, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] logger.info( 'style test correlation: %0.4f; final test correlation: %0.4f' % (ccs, cc)) np.save( os.path.join( args.test_dir, '%s_predicted_diams.npy' % os.path.split(cpmodel_path)[1]), { 'predicted_diams': predicted_diams, 'diams_style': diams_style })
def main(): parser = argparse.ArgumentParser(description='cellpose parameters') # settings for CPU vs GPU hardware_args = parser.add_argument_group("hardware arguments") hardware_args.add_argument( '--use_gpu', action='store_true', help='use gpu if torch or mxnet with cuda installed') hardware_args.add_argument('--check_mkl', action='store_true', help='check if mkl working') hardware_args.add_argument( '--mkldnn', action='store_true', help='for mxnet, force MXNET_SUBGRAPH_BACKEND = "MKLDNN"') # settings for locating and formatting images input_img_args = parser.add_argument_group("input image arguments") input_img_args.add_argument( '--dir', default=[], type=str, help='folder containing data to run or train on.') input_img_args.add_argument( '--look_one_level_down', action='store_true', help='run processing on all subdirectories of current folder') input_img_args.add_argument('--mxnet', action='store_true', help='use mxnet') input_img_args.add_argument('--img_filter', default=[], type=str, help='end string for images to run on') input_img_args.add_argument( '--channel_axis', default=None, type=int, help='axis of image which corresponds to image channels') input_img_args.add_argument( '--z_axis', default=None, type=int, help='axis of image which corresponds to Z dimension') input_img_args.add_argument( '--chan', default=0, type=int, help= 'channel to segment; 0: GRAY, 1: RED, 2: GREEN, 3: BLUE. Default: %(default)s' ) input_img_args.add_argument( '--chan2', default=0, type=int, help= 'nuclear channel (if cyto, optional); 0: NONE, 1: RED, 2: GREEN, 3: BLUE. Default: %(default)s' ) input_img_args.add_argument('--invert', action='store_true', help='invert grayscale channel') input_img_args.add_argument( '--all_channels', action='store_true', help= 'use all channels in image if using own model and images with special channels' ) # model settings model_args = parser.add_argument_group("model arguments") parser.add_argument('--pretrained_model', required=False, default='cyto', type=str, help='model to use') parser.add_argument( '--unet', required=False, default=0, type=int, help='run standard unet instead of cellpose flow output') model_args.add_argument( '--nclasses', default=3, type=int, help= 'if running unet, choose 2 or 3; if training omni, choose 4; standard Cellpose uses 3' ) # algorithm settings algorithm_args = parser.add_argument_group("algorithm arguments") parser.add_argument('--omni', action='store_true', help='Omnipose algorithm (disabled by default)') parser.add_argument( '--cluster', action='store_true', help= 'DBSCAN clustering. Reduces oversegmentation of thin features (disabled by default).' ) parser.add_argument( '--fast_mode', action='store_true', help= 'make code run faster by turning off 4 network averaging and resampling' ) parser.add_argument( '--no_resample', action='store_true', help= "disable dynamics on full image (makes algorithm faster for images with large diameters)" ) parser.add_argument('--no_net_avg', action='store_true', help='make code run faster by only running 1 network') parser.add_argument( '--no_interp', action='store_true', help='do not interpolate when running dynamics (was default)') parser.add_argument( '--do_3D', action='store_true', help='process images as 3D stacks of images (nplanes x nchan x Ly x Lx' ) parser.add_argument( '--diameter', required=False, default=30., type=float, help='cell diameter, if 0 cellpose will estimate for each image') parser.add_argument( '--stitch_threshold', required=False, default=0.0, type=float, help= 'compute masks in 2D then stitch together masks with IoU>0.9 across planes' ) algorithm_args.add_argument( '--flow_threshold', default=0.4, type=float, help= 'flow error threshold, 0 turns off this optional QC step. Default: %(default)s' ) algorithm_args.add_argument( '--mask_threshold', default=0, type=float, help= 'mask threshold, default is 0, decrease to find more and larger masks') parser.add_argument('--anisotropy', required=False, default=1.0, type=float, help='anisotropy of volume in 3D') parser.add_argument( '--diam_threshold', required=False, default=12.0, type=float, help= 'cell diameter threshold for upscaling before mask rescontruction, default 12.' ) parser.add_argument('--exclude_on_edges', action='store_true', help='discard masks which touch edges of image') # output settings output_args = parser.add_argument_group("output arguments") output_args.add_argument( '--save_png', action='store_true', help='save masks as png and outlines as text file for ImageJ') output_args.add_argument( '--save_tif', action='store_true', help='save masks as tif and outlines as text file for ImageJ') output_args.add_argument('--no_npy', action='store_true', help='suppress saving of npy') output_args.add_argument( '--savedir', default=None, type=str, help= 'folder to which segmentation results will be saved (defaults to input image directory)' ) output_args.add_argument( '--dir_above', action='store_true', help= 'save output folders adjacent to image folder instead of inside it (off by default)' ) output_args.add_argument( '--in_folders', action='store_true', help='flag to save output in folders (off by default)') output_args.add_argument( '--save_flows', action='store_true', help= 'whether or not to save RGB images of flows when masks are saved (disabled by default)' ) output_args.add_argument( '--save_outlines', action='store_true', help= 'whether or not to save RGB outline images when masks are saved (disabled by default)' ) output_args.add_argument( '--save_ncolor', action='store_true', help= 'whether or not to save minimal "n-color" masks (disabled by default') output_args.add_argument( '--save_txt', action='store_true', help='flag to enable txt outlines for ImageJ (disabled by default)') # training settings training_args = parser.add_argument_group("training arguments") training_args.add_argument('--train', action='store_true', help='train network using images in dir') training_args.add_argument('--train_size', action='store_true', help='train size network at end of training') training_args.add_argument( '--mask_filter', default='_masks', type=str, help='end string for masks to run on. Default: %(default)s') training_args.add_argument('--test_dir', default=[], type=str, help='folder containing test data (optional)') training_args.add_argument('--learning_rate', default=0.2, type=float, help='learning rate. Default: %(default)s') training_args.add_argument('--n_epochs', default=500, type=int, help='number of epochs. Default: %(default)s') training_args.add_argument('--batch_size', default=8, type=int, help='batch size. Default: %(default)s') training_args.add_argument( '--min_train_masks', default=5, type=int, help= 'minimum number of masks a training image must have to be used. Default: %(default)s' ) training_args.add_argument('--residual_on', default=1, type=int, help='use residual connections') training_args.add_argument('--style_on', default=1, type=int, help='use style vector') training_args.add_argument( '--concatenation', default=0, type=int, help= 'concatenate downsampled layers with upsampled layers (off by default which means they are added)' ) training_args.add_argument( '--save_every', default=100, type=int, help='number of epochs to skip between saves. Default: %(default)s') training_args.add_argument( '--save_each', action='store_true', help= 'save the model under a different filename per --save_every epoch for later comparsion' ) # misc settings parser.add_argument( '--verbose', action='store_true', help= 'flag to output extra information (e.g. diameter metrics) for debugging and fine-tuning parameters' ) parser.add_argument( '--testing', action='store_true', help= 'flag to suppress CLI user confirmation for saving output; for test scripts' ) args = parser.parse_args() # handle mxnet option if args.check_mkl: mkl_enabled = models.check_mkl((not args.mxnet)) else: mkl_enabled = True if not args.train and (mkl_enabled and args.mkldnn): os.environ["MXNET_SUBGRAPH_BACKEND"] = "MKLDNN" else: os.environ["MXNET_SUBGRAPH_BACKEND"] = "" if len(args.dir) == 0: if not GUI_ENABLED: print('GUI ERROR: %s' % GUI_ERROR) if GUI_IMPORT: print( 'GUI FAILED: GUI dependencies may not be installed, to install, run' ) print(' pip install cellpose[gui]') else: gui.run() else: if args.verbose: from .io import logger_setup logger, log_file = logger_setup() else: print( '>>>> !NEW LOGGING SETUP! To see cellpose progress, set --verbose' ) print('No --verbose => no progress or info printed') logger = logging.getLogger(__name__) use_gpu = False channels = [args.chan, args.chan2] # find images if len(args.img_filter) > 0: imf = args.img_filter else: imf = None # Check with user if they REALLY mean to run without saving anything if not (args.train or args.train_size): saving_something = args.save_png or args.save_tif or args.save_flows or args.save_ncolor or args.save_txt device, gpu = models.assign_device((not args.mxnet), args.use_gpu) #define available model names, right now we have three broad categories model_names = [ 'cyto', 'nuclei', 'bact', 'cyto2', 'bact_omni', 'cyto2_omni' ] builtin_model = np.any( [args.pretrained_model == s for s in model_names]) cytoplasmic = 'cyto' in args.pretrained_model nuclear = 'nuclei' in args.pretrained_model bacterial = 'bact' in args.pretrained_model # force omni on for those models, but don't toggle it off if manually specified if 'omni' in args.pretrained_model: args.omni = True if args.cluster and 'sklearn' not in sys.modules: print('>>>> DBSCAN clustering requires scikit-learn.') confirm = confirm_prompt('Install scikit-learn?') if confirm: install('scikit-learn') else: print( '>>>> scikit-learn not installed. DBSCAN clustering will be automatically disabled.' ) omni = check_omni( args.omni ) # repeat the above check but factor it for use elsewhere if args.omni: print( '>>>> Omnipose enabled. See https://raw.githubusercontent.com/MouseLand/cellpose/master/cellpose/omnipose/license.txt for licensing details.' ) if not args.train and not args.train_size: tic = time.time() if not builtin_model: cpmodel_path = args.pretrained_model if not os.path.exists(cpmodel_path): logger.warning( 'model path does not exist, using cyto model') args.pretrained_model = 'cyto' else: logger.info(f'>>> running model {cpmodel_path}') image_names = io.get_image_files( args.dir, args.mask_filter, imf=imf, look_one_level_down=args.look_one_level_down) nimg = len(image_names) cstr0 = ['GRAY', 'RED', 'GREEN', 'BLUE'] cstr1 = ['NONE', 'RED', 'GREEN', 'BLUE'] logger.info( '>>>> running cellpose on %d images using chan_to_seg %s and chan (opt) %s' % (nimg, cstr0[channels[0]], cstr1[channels[1]])) if args.omni: logger.info(f'>>>> omni is ON, cluster is {args.cluster}') # handle built-in model exceptions; bacterial ones get no size model if builtin_model: if args.mxnet: if args.pretrained_model == 'cyto2': logger.warning( 'cyto2 model not available in mxnet, using cyto model' ) args.pretrained_model = 'cyto' if bacterial: logger.warning( 'bacterial models not available in mxnet, using pytorch' ) args.mxnet = False if not bacterial: model = models.Cellpose(gpu=gpu, device=device, model_type=args.pretrained_model, torch=(not args.mxnet), omni=args.omni, net_avg=(not args.fast_mode and not args.no_net_avg)) else: cpmodel_path = models.model_path(args.pretrained_model, 0, True) model = models.CellposeModel(gpu=gpu, device=device, pretrained_model=cpmodel_path, torch=True, nclasses=args.nclasses, omni=args.omni, net_avg=False) else: if args.all_channels: channels = None model = models.CellposeModel(gpu=gpu, device=device, pretrained_model=cpmodel_path, torch=True, nclasses=args.nclasses, omni=args.omni, net_avg=False) # omni changes not implemented for mxnet. Full parity for cpu/gpu in pytorch. if args.omni and args.mxnet: logger.info('>>>> omni only implemented in pytorch.') confirm = confirm_prompt('Continue with omni set to false?') if not confirm: exit() else: logger.info('>>>> omni set to false.') args.omni = False # For now, omni version is not compatible with 3D. WIP. if args.omni and args.do_3D: logger.info( '>>>> omni not yet compatible with 3D segmentation.') confirm = confirm_prompt('Continue with omni set to false?') if not confirm: exit() else: logger.info('>>>> omni set to false.') args.omni = False # omni model needs 4 classes. Would prefer a more elegant way to automaticaly update the flow fields # instead of users deleting them manually - a check on the number of channels, maybe, or just use # the yes/no prompt to ask the user if they want their flow fields in the given directory to be deleted. # would also need the look_one_level_down optionally toggled... if args.omni and args.train: logger.info('>>>> Training omni model. Setting nclasses to 4.') logger.info( '>>>> Make sure your flow fields are deleted and re-computed.' ) args.nclasses = 4 # handle diameters if args.diameter == 0: if builtin_model: diameter = None logger.info('>>>> estimating diameter for each image') else: logger.info( '>>>> using user-specified model, no auto-diameter estimation available' ) diameter = model.diam_mean else: diameter = args.diameter logger.info('>>>> using diameter %0.2f for all images' % diameter) tqdm_out = utils.TqdmToLogger(logger, level=logging.INFO) for image_name in tqdm(image_names, file=tqdm_out): image = io.imread(image_name) out = model.eval(image, channels=channels, diameter=diameter, do_3D=args.do_3D, net_avg=(not args.fast_mode and not args.no_net_avg), augment=False, resample=(not args.no_resample and not args.fast_mode), flow_threshold=args.flow_threshold, mask_threshold=args.mask_threshold, diam_threshold=args.diam_threshold, invert=args.invert, batch_size=args.batch_size, interp=(not args.no_interp), cluster=args.cluster, channel_axis=args.channel_axis, z_axis=args.z_axis, omni=args.omni, anisotropy=args.anisotropy, verbose=args.verbose, model_loaded=True) masks, flows = out[:2] if len(out) > 3: diams = out[-1] else: diams = diameter if args.exclude_on_edges: masks = utils.remove_edge_masks(masks) if not args.no_npy: io.masks_flows_to_seg(image, masks, flows, diams, image_name, channels) if saving_something: io.save_masks(image, masks, flows, image_name, png=args.save_png, tif=args.save_tif, save_flows=args.save_flows, save_outlines=args.save_outlines, save_ncolor=args.save_ncolor, dir_above=args.dir_above, savedir=args.savedir, save_txt=args.save_txt, in_folders=args.in_folders) logger.info('>>>> completed in %0.3f sec' % (time.time() - tic)) else: if builtin_model: if args.mxnet and args.pretrained_model == 'cyto2': logger.warning( 'cyto2 model not available in mxnet, using cyto model') args.pretrained_model = 'cyto' cpmodel_path = models.model_path(args.pretrained_model, 0, not args.mxnet) if cytoplasmic: szmean = 30. elif nuclear: szmean = 17. elif bacterial: szmean = 0. #bacterial models are not rescaled else: cpmodel_path = os.fspath(args.pretrained_model) szmean = 30. test_dir = None if len(args.test_dir) == 0 else args.test_dir output = io.load_train_test_data(args.dir, test_dir, imf, args.mask_filter, args.unet, args.look_one_level_down) images, labels, image_names, test_images, test_labels, image_names_test = output # training with all channels if args.all_channels: img = images[0] if img.ndim == 3: nchan = min(img.shape) elif img.ndim == 2: nchan = 1 channels = None else: nchan = 2 # model path if not os.path.exists(cpmodel_path): if not args.train: error_message = 'ERROR: model path missing or incorrect - cannot train size model' logger.critical(error_message) raise ValueError(error_message) cpmodel_path = False logger.info('>>>> training from scratch') if args.diameter == 0: rescale = False logger.info( '>>>> median diameter set to 0 => no rescaling during training' ) else: rescale = True szmean = args.diameter else: rescale = True args.diameter = szmean logger.info('>>>> pretrained model %s is being used' % cpmodel_path) args.residual_on = 1 args.style_on = 1 args.concatenation = 0 if rescale and args.train: logger.info( '>>>> during training rescaling images to fixed diameter of %0.1f pixels' % args.diameter) # initialize model if args.unet: model = core.UnetModel(device=device, pretrained_model=cpmodel_path, diam_mean=szmean, residual_on=args.residual_on, style_on=args.style_on, concatenation=args.concatenation, nclasses=args.nclasses, nchan=nchan) else: model = models.CellposeModel(device=device, torch=(not args.mxnet), pretrained_model=cpmodel_path, diam_mean=szmean, residual_on=args.residual_on, style_on=args.style_on, concatenation=args.concatenation, nclasses=args.nclasses, nchan=nchan, omni=args.omni) # train segmentation model if args.train: cpmodel_path = model.train( images, labels, train_files=image_names, test_data=test_images, test_labels=test_labels, test_files=image_names_test, learning_rate=args.learning_rate, channels=channels, save_path=os.path.realpath(args.dir), save_every=args.save_every, save_each=args.save_each, rescale=rescale, n_epochs=args.n_epochs, batch_size=args.batch_size, min_train_masks=args.min_train_masks, omni=args.omni) model.pretrained_model = cpmodel_path logger.info('>>>> model trained and saved to %s' % cpmodel_path) # train size model if args.train_size: sz_model = models.SizeModel(cp_model=model, device=device) sz_model.train(images, labels, test_images, test_labels, channels=channels, batch_size=args.batch_size) if test_images is not None: predicted_diams, diams_style = sz_model.eval( test_images, channels=channels) if test_labels[0].ndim > 2: tlabels = [lbl[0] for lbl in test_labels] else: tlabels = test_labels ccs = np.corrcoef( diams_style, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] cc = np.corrcoef( predicted_diams, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] logger.info( 'style test correlation: %0.4f; final test correlation: %0.4f' % (ccs, cc)) np.save( os.path.join( args.test_dir, '%s_predicted_diams.npy' % os.path.split(cpmodel_path)[1]), { 'predicted_diams': predicted_diams, 'diams_style': diams_style })
def nuclei(test_root, save_root, save_figure=False): """ nuclei performance, suppfig """ ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [] aps = [] model_type = 'nuclei' masks.append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) #masks.append(np.load(os.path.join(save_root, 'unet3_residual_on_style_on_concatenation_off_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet2_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks)): aps.append(metrics.average_precision(test_labels, masks[j], threshold=thresholds)[0]) ltrf = 10 rc('font', **{'size': 6}) fig = plt.figure(figsize=(6.85/2,3.85),facecolor='w',frameon=True, dpi=300) mdl = ['cellpose', 'mask r-cnn', 'stardist', 'unet3', 'unet2'] col ='mgcyr' iimg = 25 for j in range(3): ax = plt.subplot(3,2,2*(1+j)-1) img = test_data[iimg][1] img = np.stack((img, img, img), axis=2) plt.imshow(np.clip(img[:,:,:], 0, 255)) outpix1 = utils.outlines_list(masks[j][iimg]) outpix = utils.outlines_list(test_labels[iimg]) for out in outpix: plt.plot(out[:,0], out[:,1], color='y', lw=.5) for out in outpix1: plt.plot(out[:,0], out[:,1], '--', color='r', lw=.5) plt.title(mdl[j], color=col[j], loc = 'left') plt.text(.5, 1.05, '[email protected]=%.2f'%aps[j][iimg,0], transform=ax.transAxes, fontsize=6) plt.axis('off') if j==0: plt.text(-.1, 1.2, 'b', fontsize = ltrf, transform=ax.transAxes) ax=fig.add_axes([.65, .3 ,.33,.4]) for j in range(len(mdl)): ax.plot(thresholds, aps[j].mean(axis=0), color=col[j], lw=1.) #print(aps[0][j][:11].mean(axis=0)[0]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_ylim([0, 1]) ax.set_xlim([0.5, 1]) ax.set_ylabel('average precision') ax.set_xlabel('IoU matching threshold') for j in range(len(mdl)): ax.text(.05, .32 - .075*j, mdl[j], color=col[j], fontsize=6, transform=ax.transAxes) ax.text(-.4, 1., 'c', fontsize = ltrf, transform=ax.transAxes) if save_figure: os.makedirs(os.path.join(save_root, 'figs'), exist_ok=True) fig.savefig(os.path.join(save_root, 'figs/suppfig_perf2d_nuclei.pdf'), bbox_inches='tight') return masks, aps
def cyto(test_root, save_root, save_figure=False): """ cyto performance, main fig 2 """ ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [[], []] aps = [[], []] model_types = ['cyto_sp', 'cyto'] for m,model_type in enumerate(model_types): masks[m].append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) #masks[m].append(np.load(os.path.join(save_root, 'unet3_residual_on_style_on_concatenation_off_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) masks[m].append(np.load(os.path.join(save_root, 'unet2_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks[m])): aps[m].append(metrics.average_precision(test_labels, masks[m][j], threshold=thresholds)[0]) ltrf = 10 rc('font', **{'size': 6}) fig = plt.figure(figsize=(6.85,3.75/2 * 3),facecolor='w',frameon=True, dpi=300) mdl = ['cellpose', 'mask r-cnn', 'stardist', 'unet3', 'unet2'] col ='mgcyr' iimg = 1 for j in range(3): ax = plt.subplot(3,4,2+j) img = test_data[1][1] img = np.stack((np.zeros_like(img), img, test_data[iimg][0]), axis=2) plt.imshow(np.clip(img[:,75:-75,:], 0, 255)) outpix1 = utils.outlines_list(masks[0][j][iimg][:,75:-75]) outpix = utils.outlines_list(test_labels[iimg][:,75:-75]) for out in outpix: plt.plot(out[:,0], out[:,1], color='y', lw=.5) for out in outpix1: plt.plot(out[:,0], out[:,1], '--', color='r', lw=.5) plt.title(mdl[j], color=col[j], loc = 'left') plt.text(.5, 1.05, '[email protected]=%.2f'%aps[0][j][iimg,0], transform=ax.transAxes, fontsize=7) plt.arrow(305, 110, 7, 7, color='w', head_width = 5) plt.arrow(255, 325, -10, 0, color='w', head_width = 5) plt.arrow(155, 250, 10, 0, color='w', head_width = 5) plt.arrow(315, 50, 0, -10, color='w', head_width = 5) plt.arrow(100, 220, -7, -7, color='w', head_width = 5) plt.axis('off') if j==0: plt.text(.0, 1.2, 'specialist model / specialized data', fontsize = 7, style='italic', transform=ax.transAxes) plt.text(-.1, 1.2, 'b', fontsize = ltrf, transform=ax.transAxes) iimg = 16 for j in range(3): ax = plt.subplot(3,4,6+j) img = test_data[iimg][1] img = np.stack((img, img, img), axis=2) plt.imshow(np.clip(img[:,:,:], 0, 255)) outpix1 = utils.outlines_list(masks[1][j][iimg][:,:]) outpix = utils.outlines_list(test_labels[iimg]) for out in outpix: plt.plot(out[:,0], out[:,1], color='y', lw=.5) for out in outpix1: plt.plot(out[:,0], out[:,1], '--', color='r', lw=.5) plt.title(mdl[j], color=col[j], loc = 'left', fontsize=7) plt.text(.5, 1.1, '[email protected]=%.2f'%aps[1][j][iimg,0], transform=ax.transAxes, fontsize=6) plt.axis('off') if j==0: plt.text(.0, 1.3, 'generalist model / generalized data', fontsize = 7, style='italic', transform=ax.transAxes) plt.text(-.1, 1.3, 'c', fontsize = ltrf, transform=ax.transAxes) titles = ['specialist model / \n specialized data', 'specialist model /\n generalized data', 'generalist model / \n specialized data', 'generalist model /\n generalized data'] inds = [np.arange(0,11,1,int), np.arange(11,ntest,1,int)] for t in range(4): ax = fig.add_axes([0.1+.22*t,0.1,0.17,0.25]) for j in range(len(mdl)): ap = aps[t//2][j][inds[t%2]].mean(axis=0) #print(titles[t], mdl[j], ap[0]) ax.plot(thresholds, ap, color=col[j]) #print(aps[0][j][:11].mean(axis=0)[0]) if t==2: print(mdl[j], aps[t//2][j].mean(axis=0)[0]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_ylim([0, 1]) ax.set_xlim([0.5, 1]) if t==0: plt.ylabel('average precision') ax.set_xlabel('IoU matching threshold') ax.text(0, 1.05, titles[t], fontsize = 7, ha='left', transform=ax.transAxes) ax.text(-.25, 1.15, string.ascii_lowercase[t+3], fontsize = 10, transform=ax.transAxes) if t==0: for j in range(len(mdl)): ax.text(.05, .4 - .075*j, mdl[j], color=col[j], fontsize=6, transform=ax.transAxes) ax = fig.add_axes([.05,.37,.25,.65]) img = io.imread(os.path.join(save_root, 'figs/training_schematic_final.PNG')) ax.imshow(img) ax.axis('off') ax.text(0, 1.09, 'a', fontsize = ltrf, transform=ax.transAxes) if save_figure: os.makedirs(os.path.join(save_root, 'figs'), exist_ok=True) fig.savefig(os.path.join(save_root, 'figs/fig_perf2d_cyto.pdf'), bbox_inches='tight')
def suppfig_cellpose_params(test_root, save_root, save_figure=False): ap_cellpose_all = np.load(os.path.join(save_root, 'ap_cellpose_all.npy')) rc('font', **{'size': 6}) fig=plt.figure(figsize=(3,1.5), facecolor='w',frameon=True, dpi=300) colors = [c for c in plt.get_cmap('Dark2').colors] ap_compare = ap_cellpose_all[:,0,:,0].flatten() netstr = ['style off', 'residual off', 'concatenation on', 'unet architecture', 'one net'] bmax = 0.15 dbin = 0.02 for i in range(4):#ap_cellpose_all.shape[1]-1): ax = fig.add_axes([0.1+i*0.5, 0.1, 0.38, 0.75]) if i<5: apt = ap_cellpose_all[:,i+1,:,0].flatten() else: apt = ap_cellpose_all[:,5:,:,0].mean(axis=1).flatten() diffs = apt - ap_compare ax.text(-.1, 1.1, netstr[i], transform=ax.transAxes , fontsize=7) hb = ax.hist(np.clip(diffs, -1*bmax+dbin/2, bmax-dbin/2), bins=np.arange(-bmax, bmax+dbin, dbin), color=colors[i]) max_counts = hb[0].max()*1.05 dm = np.mean(diffs) p = stats.wilcoxon(diffs).pvalue print(p) nstars = np.array([p<0.05, p<0.01, p<0.001]).sum() ax.scatter(dm, max_counts*1.025, marker='v', color=colors[i], s=10) ax.text(dm, max_counts*1.1, '%0.3f'%dm+'*'*nstars, ha='center', fontsize=6) ax.set_xlabel('difference in average precision') if i==0: ax.set_ylabel('# of test images') ax.text(-.3, 1.1, string.ascii_lowercase[i], transform=ax.transAxes, fontsize=11) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_xlim([-bmax, bmax]) #ax.set_xticks(np.arange(-0.3,0.4,0.1)) ax.set_ylim([0, max_counts*1.25]) # performance without specialized images ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [] aps = [] model_type = 'cyto' masks.append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'cellpose_%s_wo_sp_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks)): aps.append(metrics.average_precision(test_labels, masks[j], threshold=thresholds)[0]) i=4 ax = fig.add_axes([0.1+i*0.5, 0.1, 0.38, 0.75]) inds = [np.arange(11,ntest,1,int)] sstr = ['cellpose', 'cellpose w/o\nspecialized'] ls = ['-'] col = ['m', [0.3, 0, 0.3]] for t in range(len(inds)): for j in range(len(masks)): ax.plot(thresholds, aps[j][inds[t]].mean(axis=0), color=col[j], ls=ls[t]) if t==0: ax.text(1., 0.7-j*0.18, sstr[j], color=col[j], ha='right') #print(aps[0][j][:11].mean(axis=0)[0]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_ylim([0, 1]) ax.set_xlim([0.5, 1]) ax.set_ylabel('average precision') ax.set_xlabel('IoU matching threshold') ax.text(-.1, 1., 'remove specialized images\n from training set', transform=ax.transAxes , fontsize=7) ax.text(-.3, 1.1, string.ascii_lowercase[i], transform=ax.transAxes, fontsize=11) if save_figure: os.makedirs(os.path.join(save_root, 'figs'), exist_ok=True) fig.savefig(os.path.join(save_root, 'figs/suppfig_cellpose_params.pdf'), bbox_inches='tight')
def train_cellpose_nets(data_root): """ train networks on 9-folds of data (180 networks total) ... ~1 week on one GPU """ # can also run on command line for GPU cluster # python -m cellpose --train --use_gpu --dir images_cyto/train"$7"/ --test_dir images_cyto/test"$7"/ --img_filter _img --pretrained_model None --chan 2 --chan2 1 --unet "$1" --nclasses "$2" --learning_rate "$3" --residual_on "$4" --style_on "$5" --concatenation "$6" device = mx.gpu() ntest = 68 ntrain = 540 concatenation = [0, 0, 0, 1, 1] residual_on = [1, 1, 0, 1, 0] style_on = [1, 0, 1, 1, 0] channels = [2, 1] for j in range(9): # load images train_root = os.path.join(data_root, 'train%d/' % j) train_data = [ io.imread(os.path.join(train_root, '%03d_img.tif' % i)) for i in range(ntrain) ] train_labels = [ io.imread(os.path.join(train_root, '%03d_masks.tif' % i)) for i in range(ntrain) ] train_flow_labels = [ io.imread(os.path.join(train_root, '%03d_img_flows.tif' % i)) for i in range(ntrain) ] train_labels = [ np.concatenate( (train_labels[i][np.newaxis, :, :], train_flow_labels), axis=0) for i in range(ntrain) ] test_root = os.path.join(data_root, 'test%d/' % j) test_data = [ io.imread(os.path.join(test_root, '%03d_img.tif' % i)) for i in range(ntest) ] test_labels = [ io.imread(os.path.join(test_root, '%03d_masks.tif' % i)) for i in range(ntest) ] test_flow_labels = [ io.imread(os.path.join(test_root, '%03d_img_flows.tif' % i)) for i in range(ntest) ] test_labels = [ np.concatenate( (test_labels[i][np.newaxis, :, :], test_flow_labels), axis=0) for i in range(ntest) ] # train networks for k in range(len(concatenation)): # 4 nets for each for l in range(4): model = models.CellposeModel(device=device, pretrained_model=None, diam_mean=30, residual_on=residual_on[k], style_on=style_on[k], concatenation=concatenation[k]) model.train(images, labels, test_data=test_images, test_labels=test_labels, channels=channels, rescale=True, save_path=train_root) # train size network on default network once if k == 0 and l == 0: sz_model = models.SizeModel(model, device=device) sz_model.train(train_data, train_labels, test_data, test_labels, channels=channels) predicted_diams, diams_style = sz_model.eval( test_data, channels=channels) tlabels = [lbl[0] for lbl in test_labels] ccs = np.corrcoef( diams_style, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] cc = np.corrcoef( predicted_diams, np.array([utils.diameters(lbl)[0] for lbl in tlabels]))[0, 1] print( 'style test correlation: %0.4f; final test correlation: %0.4f' % (ccs, cc)) np.save( os.path.join(test_root, 'predicted_diams.npy'), { 'predicted_diams': predicted_diams, 'diams_style': diams_style })
def suppfig_metrics(test_root, save_root, save_figure=False): """ boundary / aji metrics """ ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [] bscores = [] ajis = [] model_type = 'cyto' masks.append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet2_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) scales = np.arange(0.025, 0.275, 0.025) for j in range(len(masks)): bscores.append(metrics.boundary_scores(test_labels, masks[j], scales)) ajis.append(metrics.aggregated_jaccard_index(test_labels, masks[j])) ltrf = 10 rc('font', **{'size': 6}) fig = plt.figure(figsize=(6.85,3.85),facecolor='w',frameon=True, dpi=300) mdl = ['cellpose', 'mask r-cnn', 'stardist', 'unet3', 'unet2'] col ='mgcyr' titles = ['boundary precision', 'boundary recall', 'boundary F-score', 'aggregated jaccard index'] s=0 ttr = ['specialized data', 'generalized data'] ii = [0,2,1,3] inds = [np.arange(0,11,1,int), np.arange(11,ntest,1,int)] for t in range(len(inds)): for k in range(4): ax = fig.add_axes([0.1+.22*k,0.66-t*0.5,0.14,0.28]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) if k<3: for j in range(len(mdl)): ax.plot(scales*30, bscores[j][k][:,inds[t]].mean(axis=1), color=col[j]) ax.set_ylim([0.25, 1]) ax.set_ylabel(titles[k]) ax.set_xlabel('boundary width (pixels)') if k==0: ax.text(-0.2, 1.1, ttr[t], fontsize = 7, ha='left', transform=ax.transAxes) else: for j in range(len(mdl)): ax.bar(j, ajis[j][inds[t]].mean(), color=col[j]) ax.errorbar(j, ajis[j][inds[t]].mean(), ajis[j][inds[t]].std()/ len(inds[t])**0.5, color='k') ax.text(-0.2, 1.1, ttr[t], fontsize = 7, ha='left', transform=ax.transAxes) ax.set_ylabel(titles[k]) ax.set_xticks(np.arange(0,len(mdl))) plt.xticks(rotation=90) ax.set_xticklabels(mdl) ax.set_ylim([0,1]) #ax.set_xlim([0.5, 1]) if k==0 or k==3: ax.text(-.4, 1.1, string.ascii_lowercase[ii[s]], fontsize = ltrf, transform=ax.transAxes) s+=1 if t==0 and k==0: for j in range(len(mdl)): ax.text(.5, .5 - .075*j, mdl[j], color=col[j], fontsize=6, transform=ax.transAxes) if save_figure: os.makedirs(os.path.join(save_root, 'figs'), exist_ok=True) fig.savefig(os.path.join(save_root, 'figs/suppfig_metrics.pdf'), bbox_inches='tight')
def segment_cells_nuclei_indiv(path_scan, str_channels, img_ext, new_size, model_types, diameters, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): """[summary] segment cells and nuclei in bulk, e.g. first all images are loaded and then segmented. TODO: specify parameters Parameters ---------- path_scan : [type] [description] strings : [type] [description] img_ext : [type] [description] new_size : tuple Defines resizing of image. If two elements, new size of image. If one element, resizing factor. If emtpy, no resizing. sizes : [type] [description] models : [type] [description] path_save : pathlin object or string Path to save results, - If Pathlib object, then this absolute path is used. - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). And results will be stored in subfolder 'segmentation-input' callback_log : [type], optional [description], by default None callback_status : [type], optional [description], by default None callback_progress : [type], optional [description], by default None """ par_dict = locals() par_dict = clean_par_dict(par_dict) log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) # Get parameters (str_cyto, str_nuclei) = str_channels (diameter_cells, diameter_nuclei) = diameters (model_type_cells, model_type_nuclei) = model_types # Use provided absolute user-path to save images. if isinstance(path_save, pathlib.PurePath): path_save_results = path_save if not path_save_results.is_dir(): path_save_results.mkdir(parents=True) else: path_save_str_replace = path_save # Configurations config_nuclei = { 'model_type': model_type_nuclei, 'diameter': diameter_nuclei, 'net_avg': net_avg, 'resample': resample } config_cyto = { 'model_type': model_type_cells, 'diameter': diameter_cells, 'net_avg': net_avg, 'resample': resample } channels_cyto = [1, 3] channels_nuclei = [0, 1] # Loop over data log_message(f'\nLoading image pairs and segment them on the fly', callback_fun=callback_log) if not path_scan.is_dir(): log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) return # Search for file to be analyzed log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) files_proc = [] for path_img in path_scan.rglob(f'*{str_cyto}*{img_ext}'): if input_subfolder: if path_img.parts[-2] == input_subfolder: files_proc.append(path_img) else: files_proc.append(path_img) n_imgs = len(files_proc) if n_imgs == 0: log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) return # Process files for idx, path_cyto in enumerate(files_proc): imgs_cyto = [] imgs_nuclei = [] files_cyto = [] files_nuclei = [] sizes_orginal = [] log_message(f'Segmenting image : {path_cyto.name}', callback_fun=callback_log) if callback_status: callback_status(f'Segmenting image : {path_cyto.name}') if callback_progress: progress = float((idx + 1) / n_imgs) callback_progress(progress) # DAPI image: existing? path_nuclei = Path(str(path_cyto).replace(str_cyto, str_nuclei)) if not path_nuclei.is_file(): log_message(f'DAPI image not found : {path_nuclei}', callback_fun=callback_log) continue # Read images img_cyto = io.imread(str(path_cyto)) if img_cyto.ndim != 2: log_message( f'\nERROR\n Input image of cell has to be 2D. Current image is {img_cyto.ndim}D', callback_fun=callback_log) continue img_nuclei = io.imread(str(path_nuclei)) if img_nuclei.ndim != 2: log_message( f'\nERROR\n Input image of cell has to be 2D. Current image is {img_nuclei.ndim}D', callback_fun=callback_log) continue # Resize image before CellPose if specified sizes_orginal.append(img_cyto.shape) # Resize if new_size: # New size can also be defined as a scalar factor if len(new_size) == 1: scale_factor = new_size[0] img_size = img_cyto.shape new_size = tuple(int(ti / scale_factor) for ti in img_size) # IMPORTANT: CV2 resize is defined as (width, height) dsize = (new_size[1], new_size[0]) img_cyto = cv2.resize(img_cyto, dsize) img_nuclei = cv2.resize(img_nuclei, dsize) img_zeros = np.zeros(img_cyto.shape) # For cell segmentation img_3d = np.dstack([img_cyto, img_zeros, img_nuclei]) imgs_cyto.append(img_3d) files_cyto.append(path_cyto) # For nuclei segmentation img_3d_dpi = np.dstack([img_zeros, img_zeros, img_nuclei]) imgs_nuclei.append(img_3d_dpi) files_nuclei.append(path_nuclei) # Create new output path if specified if not isinstance(path_save, pathlib.PurePath): path_save_results = create_output_path(path_cyto.parent, path_save_str_replace, subfolder='', create_path=True) path_save_settings = path_save_results # >>> Call function for prediction of cell data_cyto = { 'imgs': imgs_cyto, 'file_names': files_cyto, 'sizes_orginal': sizes_orginal, 'new_size': new_size, 'channels': channels_cyto, 'obj_name': 'cells' } cellpose_predict(data_cyto, config_cyto, path_save=path_save_results, callback_log=callback_log) # >>> Call function for prediction of nuclei data_nuclei = { 'imgs': imgs_nuclei, 'file_names': files_nuclei, 'channels': channels_nuclei, 'obj_name': 'nuclei', 'sizes_orginal': sizes_orginal, 'new_size': new_size } cellpose_predict(data_nuclei, config_nuclei, path_save=path_save_results, callback_log=callback_log) # Save settings if len(imgs_cyto) > 0: fp = open( str(path_save_results / 'segmentation_settings__cells_nuclei.json'), "w") json.dump(par_dict, fp, indent=4, sort_keys=True) fp.close() log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log)
def mask_stats(test_root, save_root, save_figure=False): """ cyto performance broken down """ ntest = len(glob(os.path.join(test_root, '*_img.tif'))) test_data = [io.imread(os.path.join(test_root, '%03d_img.tif'%i)) for i in range(ntest)] test_labels = [io.imread(os.path.join(test_root, '%03d_masks.tif'%i)) for i in range(ntest)] masks = [] aps = [] model_type = 'cyto' masks.append(np.load(os.path.join(save_root, 'cellpose_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'maskrcnn_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'stardist_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet3_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) masks.append(np.load(os.path.join(save_root, 'unet2_residual_off_style_off_concatenation_on_%s_masks.npy'%model_type), allow_pickle=True)) for j in range(len(masks)): aps.append(metrics.average_precision(test_labels, masks[j], threshold=thresholds)[0]) # compute shape measure + bin it convexities = np.zeros(0) maskinds = np.zeros(0, 'int') for i in range(len(test_labels)): _,solidity, _ = utils.get_mask_stats(test_labels[i]) convexities = np.append(convexities, solidity) maskinds = np.append(maskinds, i*np.ones(len(solidity), 'int')) if i==10: ispec = len(convexities) bins = np.array([np.percentile(convexities, ip) for ip in np.linspace(0,100,4)]) digi = np.digitize(np.clip(convexities.copy(),bins[0]+.01, bins[-1]-0.01), bins=bins) - 1 # compute IoU in shape bins nbins = 3 iou_threshold = 0.5 ioub = np.zeros((len(masks), nbins)) ioub_exc = np.zeros((len(masks), nbins)) ioub_ste = np.zeros((len(masks), nbins)) for j in range(len(masks)): iouall=np.zeros(0) for i in range(len(test_labels)): iou = metrics._intersection_over_union(test_labels[i], masks[j][i])[1:,1:] n_min = min(iou.shape[0], iou.shape[1]) costs = -(iou >= 0.5).astype(float) - iou / (2*n_min) true_ind, pred_ind = linear_sum_assignment(costs) iout = np.zeros(test_labels[i].max()) iout[true_ind] = iou[true_ind,pred_ind] iouall = np.append(iouall, iout) for d in np.unique(digi): iou_d = iouall[ispec:][digi[ispec:]==d] ioub_exc[j,d] = (iou_d<=iou_threshold).mean() iou_d = iou_d[iou_d > iou_threshold] ioub[j,d] = iou_d.mean() ioub_ste[j,d] = iou_d.std() / (digi==d).sum() ltrf = 10 rc('font', **{'size': 6}) fig = plt.figure(figsize=(6.85,3.85),facecolor='w',frameon=True, dpi=300) mdl = ['cellpose', 'mask r-cnn', 'stardist', 'unet3', 'unet2'] col ='mgcyr' titles = ['Cells : fluorescent', 'Cells : nonfluorescent', 'Cell membranes', 'Microscopy : other', 'Non-microscopy'] s=0 inds = [np.arange(11,28,1,int), np.arange(28,33,1,int), np.arange(33,42,1,int), np.arange(42,55,1,int), np.arange(55,ntest,1,int)] for t in range(len(inds)): ax = fig.add_axes([0.1+.22*t,0.66,0.14,0.28]) for j in range(len(mdl)): ax.plot(thresholds, aps[j][inds[t]].mean(axis=0), color=col[j]) #print(aps[0][j][:11].mean(axis=0)[0]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_ylim([0, 1]) ax.set_xlim([0.5, 1]) if t==0: plt.ylabel('average precision') ax.set_xlabel('IoU matching threshold') ax.text(-0.2, 1.1, titles[t], fontsize = 7, ha='left', transform=ax.transAxes) if t==0: ax.text(-.4, 1.1, string.ascii_lowercase[s], fontsize = 10, transform=ax.transAxes) s+=1 for j in range(len(mdl)): ax.text(.5, .9 - .075*j, mdl[j], color=col[j], fontsize=6, transform=ax.transAxes) # size figs # size dist for all images sz_dist = np.load(os.path.join(save_root, 'size_distribution.npy')) # ap for all images aps=np.load(os.path.join(save_root, 'ap_cellpose_all.npy')) # example low-high masks d=np.load(os.path.join(save_root, 'example_size_masks.npy'), allow_pickle=True).item() msks = d['masks'] imgs = d['imgs'] inds = d['inds'] szs = sz_dist.flatten() ap5 = aps[:,0,:,0].flatten() r,p = stats.pearsonr(szs, ap5) print(r,p) xb = np.linspace(0,1,3) bs = [300, 200] for j in range(2): ax = fig.add_axes([.07, .2-0.3*(j), 0.14,0.28]) #inds = np.nonzero(np.logical_and(sz_dist[0,:]>xb[j], sz_dist[0,:]<xb[j+1]))[0] ic = inds[j] maski = plot.mask_overlay(imgs[j], msks[j]) patch = plot.interesting_patch(msks[j], bsize=bs[j]) ax.imshow(maski[np.ix_(patch[0], patch[1])]) ax.axis('off') if j==0: ax.text(0.1, 1.05, 'low homogeneity', transform=ax.transAxes) ax.text(-.15,1.25,string.ascii_lowercase[s],transform=ax.transAxes, fontsize=10) ax.text(-.0,1.25,'within-image size variability',transform=ax.transAxes, fontsize=7) s+=1 else: ax.text(0.1, 1.05, 'high homogeneity', transform=ax.transAxes) ax = fig.add_axes([0.285, 0.04, 0.18,0.4]) ax.scatter(szs, ap5, s=0.5) slope, intercept = stats.linregress(szs, ap5)[:2] lp = szs*slope + intercept ax.plot(szs, lp, color='k', lw=1) ax.scatter(szs[inds], ap5[inds], s=40, color='r', marker='+', lw=1) ax.set_ylabel('AP @ IoU>0.5') ax.set_xlabel('homogeneity of mask areas\n(25th / 75th percentile)') ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.text(1.05, 0.63, 'r=%0.2f,\np=%0.3f'%(r, p), ha='right', transform=ax.transAxes, fontsize=6) #ax.text(-.25,1.1,string.ascii_lowercase[s],transform=ax.transAxes, fontsize=10) #ax.set_title('Within-image size variability') sstr = ['low', 'medium', 'high'] print(s) for k in range(3): ic = np.nonzero(np.logical_and(convexities<bins[k+1], convexities>bins[k]))[0] np.random.seed(20) inds = np.random.permutation(len(ic)) for l in range(24): ax = fig.add_axes([0.51+0.035*(l%8), 0.44-(3.3*k+l//8)*0.06, 0.03, 0.05]) imask = ic[inds[l]] im = maskinds[imask] imask -= np.nonzero(maskinds==im)[0][0] slices = find_objects(test_labels[im]==(imask+1)) ax.imshow(test_labels[im][slices[0]]==(imask+1), vmin=0, vmax=1, cmap='gray_r') ax.axis('off') if l==0: ax.text(0.1, 1.3,'%s'%(sstr[k]), ha='left', transform=ax.transAxes) if k==0: ax.text(-0.5, 2, string.ascii_lowercase[s], transform=ax.transAxes, fontsize=10) s+=1 ax.text(0.1, 2, 'convexity distributions', transform=ax.transAxes, fontsize=7) dx=0.44 pbins = np.arange(0,3) ax = fig.add_axes([0.42+dx, 0.04,.11,.4]) for j in range(len(masks)): ax.plot(pbins+0.04*j, ioub_exc[j], color=col[j], lw=1) ax.scatter(pbins+0.04*j, ioub_exc[j], color=col[j], s=5) #if k==1 and s==0: # ax.text(0.7,.95-0.06*(4-j), mdl[j], transform=ax.transAxes, color=col[j]) ax.set_xticks([0,1,2]) ax.set_xticklabels(['low', 'medium' , 'high']) ax.set_ylabel('miss rate') ax.set_xlabel('mask convexity') ax.text(.7, 1.1, 'IoU threshold = 0.5', transform=ax.transAxes) ax.set_ylim([0,1.]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) #ax.set_title('generalized data', fontsize=7) ax.text(-.5, 1.1, string.ascii_lowercase[s], transform=ax.transAxes, fontsize=10) s+=1 ax = fig.add_axes([0.6+dx, 0.04, .11,.4]) for j in range(len(masks)): ax.errorbar(pbins+0.04*j, ioub[j], ioub_ste[j],#np.abs(ioubi[k,j] - ioubi_ste[k,j].T), color=col[j], lw=1) ax.scatter(pbins+0.04*j, ioub[j], s=5, color=col[j]) ax.set_ylim([0.4, 1.0]) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_ylabel('average IoU of true positives') ax.set_xlabel('mask convexity') ax.set_xticks([0,1,2]) ax.set_xticklabels(['low', 'medium' , 'high']) if save_figure: os.makedirs(os.path.join(save_root, 'figs'), exist_ok=True) fig.savefig(os.path.join(save_root, 'figs/fig_stats.pdf'), bbox_inches='tight')
def folder_prepare_prediction(path_process, channel_ident, img_ext, path_save, projection_type, subfolder=None, search_recursive=False, callback_log=None, callback_status=None, callback_progress=None): """[summary] Parameters ---------- path_process : [type] [description] channel_ident : str [description] img_ext : str Image extension path_save : pathlin object or string Path to save results, - If Pathlib object, then this absolute path is used. - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). And results will be stored in subfolder 'segmentation-input' projection_type : [type] [description] subfolder: str subfolder where data should be stored. Will only be used when string replacement for path is used. search_recursive : bool Recursively search folder, default: false. callback_log : [type], optional [description], by default None callback_status : [type], optional [description], by default None callback_progress : [type], optional [description], by default None """ # Print all input parameters log_message(f"Function (segment_obj_indiv) called with: {str(locals())} ", callback_fun=callback_log) # Use provided absolute user-path to save images. if isinstance(path_save, pathlib.PurePath): path_save_results = path_save if not path_save_results.is_dir(): path_save_results.mkdir(parents=True) path_save_settings = path_save_results else: path_save_str_replace = path_save # How to look for files files_proc = [] if search_recursive: for path_img in path_process.rglob(f'*{channel_ident}*{img_ext}'): files_proc.append(path_img) else: for path_img in path_process.glob(f'*{channel_ident}*{img_ext}'): files_proc.append(path_img) # Process files n_files = len(files_proc) for idx, file_proc in enumerate(files_proc): log_message(f'\n>>> Processing file: {file_proc}', callback_fun=callback_status) if callback_progress: progress = float((idx + 1) / n_files) callback_progress(progress) name_base = file_proc.stem # Create new output path if specified if not isinstance(path_save, pathlib.PurePath): path_save_results = create_output_path(file_proc.parent, path_save_str_replace, subfolder=subfolder, create_path=True) log_message(f'Results will be save here : {path_save_results}', callback_fun=callback_status) path_save_settings = path_save_results # Create subfolder when processing individual images if projection_type == 'indiv': path_save_indiv = path_save_results / name_base if not path_save_indiv.is_dir(): path_save_indiv.mkdir(parents=True) path_save_settings = path_save_indiv # Open image print(file_proc) img = imread(str(file_proc)) img_properties = { "file_process": str(file_proc), "img_name": file_proc.name, "img_path": str(file_proc.parent), "channel_ident": channel_ident, "projection_type": projection_type } name_json = path_save_settings / f'img-prop__{name_base}.json' with open(name_json, 'w') as fp: json.dump(img_properties, fp, sort_keys=True, indent=4) # Process depending specified option if projection_type == 'indiv': for i in range(img.shape[0]): name_save = path_save_indiv / f'{name_base}_Z{str(i+1).zfill(3)}.png' if name_save.is_file(): log_message( f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) imsave(str(name_save), img[i, :, :]) else: if projection_type == 'mean': img_proj = img.mean(axis=0) elif projection_type == 'max': img_proj = img.max(axis=0) name_save = path_save_results / f'{name_base}.png' if name_save.is_file(): log_message( f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) imsave(str(name_save), img_proj.astype('uint16'))