def optimize(imagefiles, lineageframes, lineagefile, args, config): # optimize normally and copy to my data structures lineage = build_initial_lineage(imagefiles, lineageframes, args, config) realimages = [ optimization.load_image(imagefile) for imagefile in imagefiles ] shape = realimages[0].shape diffimages = [] for frame_index, realimage in enumerate(realimages): synthimage = optimization.generate_synthetic_image( lineage.frames[frame_index].nodes, shape, grey_synthetic_image) diffimages.append(realimage - synthimage) # simulated annealing run_count = 2000 * lineage.total_cell_count temperature = args.temp end_temperature = args.endtemp alpha = (end_temperature / temperature)**(1 / run_count) bad_count = 0 bad_accepted = 0 for iteration in range(run_count): frame_index = lineage.choose_random_frame_index() frame = lineage.frames[frame_index] node = random.choice(frame.nodes) change = None if frame_index < len(lineage.frames) - 1 and random.random() < 1 / 3: change = Combination(node, config, diffimages[frame_index + 1], lineage.frames[frame_index + 1]) if not change.is_valid: change = None if not change and frame_index < len( lineage.frames) - 1 and random.random() < 2 / 3: change = Split(node, config, diffimages[frame_index + 1], lineage.frames[frame_index + 1]) if not change.is_valid: change = None if not change: change = Perturbation(node, config, diffimages[frame_index]) if not change.is_valid: continue # apply if acceptable costdiff = change.costdiff if costdiff <= 0: acceptance = 1.0 else: bad_count += 1 acceptance = np.exp(-costdiff / temperature) if acceptance > random.random(): if acceptance < 1: bad_accepted += 1 change.apply() if iteration % 1000 == 0 and bad_count > 0: print('pbad:', bad_accepted / bad_count) bad_count = bad_accepted = 0 temperature *= alpha save_output(imagefiles, realimages, lineage, args)
def main(args): """Main function of cellanneal.""" if (args.start_temp is not None or args.end_temp is not None) and args.auto_temp == 1: raise Exception( "when auto_temp is set to 1(default value), starting temperature or ending temperature should not be set manually" ) # if not args.no_parallel: # import dask # from dask.distributed import Client, LocalCluster # if not args.cluster: # cluster = LocalCluster( # n_workers=args.workers,local_dir="/tmp/CellUniverse/dask-worker-space" # ) # else: # cluster = args.cluster # # client = Client(cluster) # else: client = None lineagefile = None start = time.time() try: config = load_config(args.config) simulation_config = config["simulation"] #Maybe better to store the image type in the config file in the first place, instead of using cmd? if args.graySynthetic == True: simulation_config["image.type"] = "graySynthetic" elif args.phaseContrast == True: simulation_config["image.type"] = "phaseContrastImage" elif args.binary == True: simulation_config["image.type"] = "binary" else: raise ValueError( "Invalid Command: Synthetic image type must be specified") if args.debug: debugmode = True celltype = config['global.cellType'].lower() # setup the colony from a file with the initial properties lineageframes = LineageFrames() colony = lineageframes.forward() load_colony(colony, args.initial, config) cost_diff = (-1, -1) # open the lineage file for writing lineagefile = open(args.output / 'lineage.csv', 'w') header = ['file', 'name'] if celltype == 'bacilli': header.extend(['x', 'y', 'width', 'length', 'rotation']) print(','.join(header), file=lineagefile) #optimze configuration config["simulation"] = find_optimal_simulation_conf( config["simulation"], load_image(get_inputfiles(args)[0]), list(colony)) if args.global_optimization: import global_optimization if args.auto_temp == 1: print("auto temperature schedule started") args.start_temp, args.end_temp = \ global_optimization.auto_temp_schedule(get_inputfiles(args), lineageframes, lineagefile, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) global_optimization.optimize(get_inputfiles(args), lineageframes, lineagefile, args, config) return 0 if args.auto_temp == 1: print("auto temperature schedule started") args.start_temp, args.end_temp = auto_temp_schedule( get_inputfiles(args)[0], lineageframes.forward(), args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) frame_num = 0 prev_cell_num = len(colony) for imagefile in get_inputfiles( args): # Recomputing temperature when needed frame_num += 1 if args.auto_meth == "frame": if auto_temp_schedule_frame(frame_num, 8): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = auto_temp_schedule( imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) elif args.auto_meth == "factor": if auto_temp_schedule_factor(len(colony), prev_cell_num, 1.1): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = auto_temp_schedule( imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) prev_cell_num = len(colony) elif args.auto_meth == "const": if auto_temp_schedule_const(len(colony), prev_cell_num, 10): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = auto_temp_schedule( imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) prev_cell_num = len(colony) elif args.auto_meth == "cost": print(cost_diff, frame_num, auto_temp_shcedule_cost(cost_diff)) if frame_num >= 2 and auto_temp_shcedule_cost(cost_diff): print( "auto temperature schedule started cost_diff (recomputed)" ) args.start_temp, args.end_temp = auto_temp_schedule( imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) colony = optimize(imagefile, lineageframes, args, config, client) cost_diff = update_cost_diff(colony, cost_diff) # flatten modifications and save cell properties colony.flatten() for cellnode in colony: properties = [imagefile.name, cellnode.cell.name] if celltype == 'bacilli': properties.extend([ str(cellnode.cell.x), str(cellnode.cell.y), str(cellnode.cell.width), str(cellnode.cell.length), str(cellnode.cell.rotation) ]) print(','.join(properties), file=lineagefile) except KeyboardInterrupt as error: raise error finally: if lineagefile: lineagefile.close() print(f'{time.time() - start} seconds') return 0
def optimize(imagefiles, lineageframes, lineagefile, args, config): global grey_synthetic_image, useDistanceObjective grey_synthetic_image = args.graysynthetic useDistanceObjective = args.dist # optimize normally and copy to my data structures lineage = build_initial_lineage(imagefiles, lineageframes, args, config) realimages = [ optimization.load_image(imagefile) for imagefile in imagefiles ] shape = realimages[0].shape diffimages = [] distmaps = [] if not useDistanceObjective: distmaps = [None] * len(realimages) for frame_index, realimage in enumerate(realimages): synthimage = optimization.generate_synthetic_image( lineage.frames[frame_index].nodes, shape, grey_synthetic_image) diffimages.append(realimage - synthimage) if useDistanceObjective: distmap = distance_transform_edt(realimage < .5) distmap /= config[ f'{config["global.cellType"].lower()}.distanceCostDivisor'] * config[ 'global.pixelsPerMicron'] distmap += 1 distmaps.append(distmap) # simulated annealing run_count = 500 * lineage.total_cell_count temperature = args.start_temp end_temperature = args.end_temp alpha = (end_temperature / temperature)**(1 / run_count) bad_count = 0 bad_accepted = 0 for iteration in range(run_count): frame_index = lineage.choose_random_frame_index() frame = lineage.frames[frame_index] node = random.choice(frame.nodes) change = None if frame_index < len(lineage.frames) - 1 and random.random() < 1 / 3: change = Combination(node, config, diffimages[frame_index + 1], lineage.frames[frame_index + 1], distmaps[frame_index + 1]) if not change.is_valid: change = None if not change and frame_index < len( lineage.frames) - 1 and random.random() < 2 / 3: change = Split(node, config, diffimages[frame_index + 1], lineage.frames[frame_index + 1], distmaps[frame_index + 1]) if not change.is_valid: change = None if not change: change = Perturbation(node, config, diffimages[frame_index], distmaps[frame_index]) if not change.is_valid: continue # apply if acceptable costdiff = change.costdiff if costdiff <= 0: acceptance = 1.0 else: bad_count += 1 acceptance = np.exp(-costdiff / temperature) if acceptance > random.random(): if acceptance < 1: bad_accepted += 1 change.apply() temperature *= alpha save_output(imagefiles, realimages, lineage, args, lineagefile)
def main(args): """Main function of cellanneal.""" if (args.start_temp is not None or args.end_temp is not None) and args.auto_temp == 1: raise Exception("when auto_temp is set to 1(default value), starting temperature or ending temperature should not be set manually") # if not args.no_parallel: # import dask # from dask.distributed import Client, LocalCluster # if not args.cluster: # cluster = LocalCluster( # n_workers=args.workers,local_dir="/tmp/CellUniverse/dask-worker-space" # ) # else: # cluster = args.cluster # # client = Client(cluster) # else: client = None lineagefile = None start = time.time() try: config = load_config(args.config) simulation_config = config["simulation"] #Maybe better to store the image type in the config file in the first place, instead of using cmd? if args.graySynthetic == True: simulation_config["image.type"] = "graySynthetic" elif args.phaseContrast == True: simulation_config["image.type"] = "phaseContrastImage" elif args.binary == True: simulation_config["image.type"] = "binary" else: raise ValueError("Invalid Command: Synthetic image type must be specified") if not args.output.is_dir(): args.output.mkdir() if not args.bestfit.is_dir(): args.bestfit.mkdir() if args.residual and not args.residual.is_dir(): args.residual.mkdir() seed = int(start * 1000) % (2**32) if args.seed != None: seed = args.seed np.random.seed(seed) print("Seed: {}".format(seed)) celltype = config['global.cellType'].lower() # setup the colony from a file with the initial properties lineageframes = LineageFrames() colony = lineageframes.forward() imagefiles = get_inputfiles(args) if args.lineage_file: load_colony(colony, args.lineage_file, config, initial_frame = imagefiles[0].name) else: load_colony(colony, args.initial, config) cost_diff = (-1, -1) # open the lineage file for writing lineagefile = open(args.output/'lineage.csv', 'w') header = ['file', 'name'] if celltype == 'bacilli': header.extend(['x', 'y', 'width', 'length', 'rotation', "split_alpha", "opacity"]) print(','.join(header), file=lineagefile) if args.debug: with open(args.debug/'debug.csv', 'w') as debugfile: print(','.join(['window_start', 'window_end', 'pbad_total', 'bad_count', 'temperature', 'total_cost_diff', 'current_iteration', 'total_iterations']), file=debugfile) if args.global_optimization: global useDistanceObjective useDistanceObjective = args.dist realimages = [optimization.load_image(imagefile) for imagefile in imagefiles] window = config["global_optimizer.window_size"] if args.lineage_file: lineage = global_optimization.build_initial_lineage(imagefiles, args.lineage_file, args.continue_from, config["simulation"]) else: lineage = global_optimization.build_initial_lineage(imagefiles, args.initial, args.continue_from, config["simulation"]) lineage = global_optimization.find_optimal_simulation_confs(imagefiles, lineage, realimages, args.continue_from) sim_start = args.continue_from - args.frame_first print(sim_start) shape = realimages[0].shape synthimages = [] cellmaps = [] distmaps = [] iteration_per_cell = config["iteration_per_cell"] if not useDistanceObjective: distmaps = [None] * len(realimages) for window_start in range(1 - window, len(realimages)): window_end = window_start + window print(window_start, window_end) if window_end <= len(realimages): # get initial estimate if window_start >= sim_start: if window_end > 1: lineage.copy_forward() realimage = realimages[window_end - 1] synthimage, cellmap = optimization.generate_synthetic_image(lineage.frames[window_end - 1].nodes, shape, lineage.frames[window_end - 1].simulation_config) synthimages.append(synthimage) cellmaps.append(cellmap) if useDistanceObjective: distmap = distance_transform_edt(realimage < .5) distmap /= config[f'{config["global.cellType"].lower()}.distanceCostDivisor'] * config[ 'global.pixelsPerMicron'] distmap += 1 distmaps.append(distmap) if args.auto_temp == 1 and window_end == 1: print("auto temperature schedule started") args.start_temp, args.end_temp = \ global_optimization.auto_temp_schedule(imagefiles, lineage, realimages, synthimages, cellmaps, distmaps, 0, 1, lineagefile, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) if args.auto_meth == "frame" and optimization.auto_temp_schedule_frame(window_end, 3): print("auto temperature schedule restarted") args.start_temp, args.end_temp = \ global_optimization.auto_temp_schedule(imagefiles, lineage, realimages, synthimages, cellmaps, distmaps, window_start, window_end, lineagefile, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) if window_start >= sim_start: if useDistanceObjective: global_optimization.totalCostDiff = optimization.dist_objective(realimage, synthimage, distmap, cellmap, config["overlap.cost"]) else: global_optimization.totalCostDiff = optimization.objective(realimage, synthimage, cellmap, config["overlap.cost"], config["cell.importance"]) global_optimization.optimize(imagefiles, lineage, realimages, synthimages, cellmaps, distmaps, window_start, window_end, lineagefile, args, config, iteration_per_cell) if window_start >= 0: global_optimization.save_lineage(imagefiles[window_start].name, lineage.frames[window_start].nodes, lineagefile) global_optimization.save_output(imagefiles[window_start].name, synthimages[window_start], realimages[window_start], lineage.frames[window_start].nodes, args, config) return 0 config["simulation"] = optimization.find_optimal_simulation_conf(config["simulation"], optimization.load_image(imagefiles[0]), list(colony)) if args.auto_temp == 1: print("auto temperature schedule started") args.start_temp, args.end_temp = optimization.auto_temp_schedule(imagefiles[0], lineageframes.forward(), args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) frame_num = 0 prev_cell_num = len(colony) for imagefile in imagefiles: # Recomputing temperature when needed frame_num += 1 if args.auto_meth == "frame": if optimization.auto_temp_schedule_frame(frame_num, 8): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = optimization.auto_temp_schedule(imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) elif args.auto_meth == "factor": if optimization.auto_temp_schedule_factor(len(colony), prev_cell_num, 1.1): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = optimization.auto_temp_schedule(imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) prev_cell_num = len(colony) elif args.auto_meth == "const": if optimization.auto_temp_schedule_const(len(colony), prev_cell_num, 10): print("auto temperature schedule started (recomputed)") args.start_temp, args.end_temp = optimization.auto_temp_schedule(imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) prev_cell_num = len(colony) elif args.auto_meth == "cost": print(cost_diff, frame_num, optimization.auto_temp_shcedule_cost(cost_diff)) if frame_num >= 2 and optimization.auto_temp_shcedule_cost(cost_diff): print("auto temperature schedule started cost_diff (recomputed)") args.start_temp, args.end_temp = optimization.auto_temp_schedule(imagefile, colony, args, config) print("auto temperature schedule finished") print("starting temperature is ", args.start_temp, "ending temperature is ", args.end_temp) colony = optimize(imagefile, lineageframes, args, config, client) cost_diff = optimization.update_cost_diff(colony, cost_diff) # flatten modifications and save cell properties colony.flatten() for cellnode in colony: properties = [imagefile.name, cellnode.cell.name] if celltype == 'bacilli': properties.extend([ str(cellnode.cell.x), str(cellnode.cell.y), str(cellnode.cell.width), str(cellnode.cell.length), str(cellnode.cell.rotation)]) print(','.join(properties), file=lineagefile) except KeyboardInterrupt as error: raise error finally: if lineagefile: lineagefile.close() print(f'{time.time() - start} seconds') return 0
def optimize(imagefiles, lineageframes, lineagefile, args, config, iteration_per_cell=6000, in_auto_temp_schedule=False, const_temp=None): global useDistanceObjective useDistanceObjective = args.dist if not args.output.is_dir(): args.output.mkdir() if not args.bestfit.is_dir(): args.bestfit.mkdir() if args.residual and not args.output.is_dir(): args.residual.mkdir() lineage = build_initial_lineage(imagefiles, lineageframes, args, config) realimages = [ optimization.load_image(imagefile) for imagefile in imagefiles ] shape = realimages[0].shape synthimages = [] cellmaps = [] distmaps = [] pbad_total = 0 window = config["global_optimizer.window_size"] perturbation_prob = config["prob.perturbation"] combine_prob = config["prob.combine"] split_prob = config["prob.split"] background_offset_prob = config["perturbation"]["prob.background_offset"] residual_vmin = config["residual.vmin"] residual_vmax = config["residual.vmax"] if args.residual: colormap = cm.ScalarMappable(norm=Normalize(vmin=residual_vmin, vmax=residual_vmax), cmap="bwr") if not useDistanceObjective: distmaps = [None] * len(realimages) for window_start in range(1 - window, len(realimages)): window_end = window_start + window print(window_start, window_end) if window_end <= len(realimages): # get initial estimate if window_end > 1: lineage.copy_forward() # add next diffimage realimage = realimages[window_end - 1] synthimage, cellmap = optimization.generate_synthetic_image( lineage.frames[-1].nodes, shape, lineage.frames[-1].simulation_config) synthimages.append(synthimage) cellmaps.append(cellmap) if useDistanceObjective: distmap = distance_transform_edt(realimage < .5) distmap /= config[ f'{config["global.cellType"].lower()}.distanceCostDivisor'] * config[ 'global.pixelsPerMicron'] distmap += 1 distmaps.append(distmap) # simulated annealing run_count = iteration_per_cell * lineage.count_cells_in( window_start, window_end) // window print(run_count) bad_count = 0 for iteration in range(run_count): frame_index = lineage.choose_random_frame_index( window_start, window_end) if in_auto_temp_schedule: temperature = const_temp else: frame_start_temp = gerp(args.end_temp, args.start_temp, (frame_index - window_start + 1) / window) frame_end_temp = gerp(args.end_temp, args.start_temp, (frame_index - window_start) / window) temperature = gerp(frame_start_temp, frame_end_temp, iteration / (run_count - 1)) frame = lineage.frames[frame_index] node = random.choice(frame.nodes) change_option = np.random.choice( ["split", "perturbation", "combine", "background_offset"], p=[ split_prob, perturbation_prob, combine_prob, background_offset_prob ]) change = None if change_option == "split" and random.random( ) < optimization.split_proba(node.cell.length) and frame_index > 0: change = Split(node.parent, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], lineage.frames[frame_index], distmaps[frame_index]) elif change_option == "perturbation": change = Perturbation(node, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], lineage.frames[frame_index], distmaps[frame_index]) elif change_option == "combine" and frame_index > 0: change = Combination(node.parent, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], lineage.frames[frame_index], distmaps[frame_index]) elif change_option == "background_offset" and frame_index > 0 and config[ "simulation"]["image.type"] == "graySynthetic": change = BackGround_luminosity_offset( lineage.frames[frame_index], realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], config) if change and change.is_valid: # apply if acceptable costdiff = change.costdiff if costdiff <= 0: acceptance = 1.0 else: bad_count += 1 acceptance = np.exp(-costdiff / temperature) pbad_total += acceptance if acceptance > random.random(): change.apply() if in_auto_temp_schedule: print("pbad is ", pbad_total / bad_count) return pbad_total / bad_count #output module if window_start >= 0: bestfit_frame = Image.fromarray( np.uint8(255 * synthimages[window_start]), "L") bestfit_frame.save(args.bestfit / imagefiles[window_start].name) output_frame = np.empty((realimages[frame_index].shape[0], realimages[frame_index].shape[1], 3)) output_frame[..., 0] = realimages[frame_index] output_frame[..., 1] = output_frame[..., 0] output_frame[..., 2] = output_frame[..., 0] for node in lineage.frames[frame_index].nodes: node.cell.drawoutline(output_frame, (1, 0, 0)) output_frame = Image.fromarray(np.uint8(255 * output_frame)) output_frame.save(args.output / imagefiles[window_start].name) if args.residual: residual_frame = Image.fromarray( np.uint8(255 * colormap.to_rgba( np.clip( realimages[window_start] - synthimages[window_start], residual_vmin, residual_vmax))), "RGBA") residual_frame.save(args.residual / imagefiles[window_start].name) save_output(imagefiles, realimages, synthimages, cellmaps, lineage, args, lineagefile, config)
def optimize(imagefiles, lineageframes, lineagefile, args, config, window=5): global useDistanceObjective useDistanceObjective = args.dist lineage = build_initial_lineage(imagefiles, lineageframes, args, config) realimages = [optimization.load_image(imagefile) for imagefile in imagefiles] shape = realimages[0].shape synthimages = [] cellmaps = [] distmaps = [] if not useDistanceObjective: distmaps = [None] * len(realimages) for window_start in range(1 - window, len(realimages)): window_end = window_start + window print(window_start, window_end) if window_end <= len(realimages): # get initial estimate if window_end > 1: lineage.copy_forward() # add next diffimage realimage = realimages[window_end - 1] synthimage, cellmap = optimization.generate_synthetic_image(lineage.frames[-1].nodes, shape, config["simulation"]) synthimages.append(synthimage) cellmaps.append(cellmap) if useDistanceObjective: distmap = distance_transform_edt(realimage < .5) distmap /= config[f'{config["global.cellType"].lower()}.distanceCostDivisor'] * config[ 'global.pixelsPerMicron'] distmap += 1 distmaps.append(distmap) # simulated annealing run_count = 2000*lineage.count_cells_in(window_start, window_end)//window print(run_count) bad_count = 0 bad_accepted = 0 for iteration in range(run_count): frame_index = lineage.choose_random_frame_index(window_start, window_end) frame_start_temp = gerp(args.end_temp, args.start_temp, (frame_index - window_start + 1)/window) frame_end_temp = gerp(args.end_temp, args.start_temp, (frame_index - window_start)/window) temperature = gerp(frame_start_temp, frame_end_temp, iteration/(run_count - 1)) frame = lineage.frames[frame_index] node = random.choice(frame.nodes) change = None if frame_index > 0 and random.random() < 1/3: change = Combination(node.parent, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], lineage.frames[frame_index], distmaps[frame_index]) if not change.is_valid: change = None if not change and frame_index > 0 and random.random() < 2/3: change = Split(node.parent, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], lineage.frames[frame_index], distmaps[frame_index]) if not change.is_valid: change = None if not change: change = Perturbation(node, config, realimages[frame_index], synthimages[frame_index], cellmaps[frame_index], distmaps[frame_index]) if not change.is_valid: continue # apply if acceptable costdiff = change.costdiff if costdiff <= 0: acceptance = 1.0 else: bad_count += 1 acceptance = np.exp(-costdiff / temperature) if acceptance > random.random(): if acceptance < 1: bad_accepted += 1 change.apply() save_output(imagefiles, realimages, cellmaps, lineage, args, lineagefile, config['simulation'])