def main(argv=None): parser = argparse.ArgumentParser(description='microscope server control') parser.add_argument('--debug', action='store_true', help='show full stack traces on error') subparsers = parser.add_subparsers(help='sub-command help', dest='command') subparsers.required = True parser_start = subparsers.add_parser('start', help='start the microscope server, if not running') parser_start.add_argument('--local', action='store_false', dest='public', help='Allow only local connections to the server [default: allow network connections]') parser_start.add_argument('--verbose', action='store_true', help='Log all RPC calls and property state changes.') parser_stop = subparsers.add_parser('stop', help='stop the microscope server, if running') parser_stop.add_argument('-f', '--force', action='store_true', help='forcibly kill the server process') parser_restart = subparsers.add_parser('restart', help='restart the microscope server, if running') parser_restart.add_argument('-f', '--force', action='store_true', help='forcibly kill the server process') parser_status = subparsers.add_parser('status', help='report whether the microscope server is running') args = parser.parse_args(argv) try: server = scope_server.ScopeServer() if args.command == 'status': server.status() if args.command in {'stop', 'restart'}: server.stop(args.force) if args.command == 'start': with server.arg_file.open('w') as f: datafile.json_encode_legible_to_file(dict(public=args.public, verbose=args.verbose), f) if args.command in {'start', 'restart'}: server.start() except Exception as e: if args.debug: traceback.print_exc(file=sys.stderr) else: sys.stderr.write(str(e)+'\n') return 1
def take_sequential_images(scope, out_dir, tl_intensity=255, exposure_time=2): ''' Take sequential images as one specifies positions on a stage ''' out_dir = pathlib.Path(out_dir) scope.camera.acquisition_sequencer.new_sequence() scope.camera.acquisition_sequencer.add_step(exposure_time, 'TL', tl_intensity=tl_intensity) out_dir.mkdir(exist_ok=True) pos_num = 0 print( 'Press enter after each position has been found; press control-c to end' ) while True: try: input() except KeyboardInterrupt: break bf_image = scope.camera.acquisition_sequencer.run()[0] freeimage.write(bf_image, out_dir / f'_{pos_num:03d}.png') pos_num += 1 if pos_num > 0: imaging_parameters = { 'lamp': 'TL', 'exposure': exposure_time, 'intensity': tl_intensity } with (out_dir / 'imaging_parameters.json').open('w') as param_file: datafile.json_encode_legible_to_file(imaging_parameters, param_file)
def reset_positions_with_offset(experiment_dir, offset): ''' Modify position coordinates based on a fixed x-y offset Parameters: experiment_dir - str/pathlib.Path to experiment root offset - list giving the x-y offset ''' experiment_dir = pathlib.Path(experiment_dir) print(f'Modifying positions for {experiment_dir.name}') metadata = load_data.read_metadata(experiment_dir) new_metadata = metadata.copy() if len(offset) == 2: offset.extend([0]) try: time_label = time.strftime('%Y%m%d-%H%M-%S') for position in metadata['positions']: position_coords = metadata['positions'][position] new_metadata['positions'][position] = [ position_coords[0] + offset[0], position_coords[1] + offset[1], position_coords[2] + offset[2] ] with (experiment_dir / f'experiment_metadata_beforechangingpositions_{time_label}.json' ).open('w') as mdata_file: datafile.json_encode_legible_to_file(metadata, mdata_file) load_data.write_metadata(new_metadata, experiment_dir) except KeyboardInterrupt: pass
def main(argv=None): parser = argparse.ArgumentParser(description='microscope server control') parser.add_argument('--debug', action='store_true', help='show full stack traces on error') subparsers = parser.add_subparsers(help='sub-command help', dest='command') subparsers.required = True parser_start = subparsers.add_parser( 'start', help='start the microscope server, if not running') parser_start.add_argument( '--local', action='store_false', dest='public', help= 'Allow only local connections to the server [default: allow network connections]' ) parser_start.add_argument( '--verbose', action='store_true', help='Log all RPC calls and property state changes.') parser_stop = subparsers.add_parser( 'stop', help='stop the microscope server, if running') parser_stop.add_argument('-f', '--force', action='store_true', help='forcibly kill the server process') parser_restart = subparsers.add_parser( 'restart', help='restart the microscope server, if running') parser_restart.add_argument('-f', '--force', action='store_true', help='forcibly kill the server process') parser_status = subparsers.add_parser( 'status', help='report whether the microscope server is running') args = parser.parse_args(argv) try: server = scope_server.ScopeServer() if args.command == 'status': server.status() if args.command in {'stop', 'restart'}: server.stop(args.force) if args.command == 'start': with server.arg_file.open('w') as f: datafile.json_encode_legible_to_file( dict(public=args.public, verbose=args.verbose), f) if args.command in {'start', 'restart'}: server.start() except Exception as e: if args.debug: traceback.print_exc(file=sys.stderr) else: sys.stderr.write(str(e) + '\n') return 1
def create_metadata_file(data_dir, positions, z_max, reference_positions, nominal_temperature, objective, optocoupler, filter_cube, fluorescence_flatfield_lamp, save_focus_stacks=None, **other_metadata): """ Create the experiment_metadata.json file for timecourse acquisitions. Parameters: data_dir: directory to write metadata file into positions: list of (x,y,z) positions, OR dict mapping different category names to lists of (x,y,z) positions. z_max: maximum z-value allowed during autofocus reference_positions: list of (x,y,z) positions to be used to generate brightfield and optionally fluorescence flat-field images. nominal_temperature: intended temperature for experiment objective, optocoupler: objective and optocoupler to be used. filter_cube: name of the filter cube to use fluorescence_flatfield_lamp: if fluorescent flatfield images are desired, provide the name of an appropriate spectra x lamp that is compatible with the specified filter cube. Use None to disable fluorescence flatfielding. save_focus_stacks: if None, don't save any focus stacks. If a list of position names, save focus stacks for those positions. If a number, save focus stacks for that number of positions. **other_metadata: key-values that will be saved directly in metadata json. """ data_dir = pathlib.Path(data_dir) data_dir.mkdir(parents=True, exist_ok=True) try: items = positions.items() except AttributeError: items = [('', positions)] named_positions = {} for name_prefix, positions in items: names = _name_positions(len(positions), name_prefix) named_positions.update(zip(names, positions)) bf_meter_position_name = _choose_bf_metering_pos(named_positions) metadata = dict(z_max=z_max, reference_positions=reference_positions, positions=named_positions, bf_meter_position_name=bf_meter_position_name, objective=int(objective), optocoupler=float(optocoupler), nominal_temperature=float(nominal_temperature), filter_cube=str(filter_cube), fluorescence_flatfield_lamp=fluorescence_flatfield_lamp, **other_metadata) if save_focus_stacks: try: # check if it's iterable for pos in save_focus_stacks: assert pos in named_positions except TypeError: num_to_save = int(save_focus_stacks) save_indices = numpy.random.permutation(len(named_positions))[:num_to_save] names = list(named_positions.keys()) save_focus_stacks = [names[i] for i in save_indices] metadata['save_focus_stacks'] = save_focus_stacks with (data_dir / 'experiment_metadata.json').open('w') as f: datafile.json_encode_legible_to_file(metadata, f)
def create_metadata_file(data_dir, positions, z_max, reference_positions, nominal_temperature, objective, optocoupler, filter_cube, fluorescence_flatfield_lamp, save_focus_stacks=None, **other_metadata): """ Create the experiment_metadata.json file for timecourse acquisitions. Parameters: data_dir: directory to write metadata file into positions: list of (x,y,z) positions, OR dict mapping different category names to lists of (x,y,z) positions. z_max: maximum z-value allowed during autofocus reference_positions: list of (x,y,z) positions to be used to generate brightfield and optionally fluorescence flat-field images. nominal_temperature: intended temperature for experiment objective, optocoupler: objective and optocoupler to be used. filter_cube: name of the filter cube to use fluorescence_flatfield_lamp: if fluorescent flatfield images are desired, provide the name of an appropriate spectra x lamp that is compatible with the specified filter cube. Use None to disable fluorescence flatfielding. save_focus_stacks: if None, don't save any focus stacks. If a list of position names, save focus stacks for those positions. If a number, save focus stacks for that number of positions. **other_metadata: key-values that will be saved directly in metadata json. """ data_dir = pathlib.Path(data_dir) data_dir.mkdir(parents=True, exist_ok=True) try: items = positions.items() except AttributeError: items = [('', positions)] named_positions = {} for name_prefix, positions in items: names = _name_positions(len(positions), name_prefix) named_positions.update(zip(names, positions)) bf_meter_position_name = _choose_bf_metering_pos(named_positions) metadata = dict(z_max=z_max, reference_positions=reference_positions, positions=named_positions, bf_meter_position_name=bf_meter_position_name, objective=int(objective), optocoupler=float(optocoupler), nominal_temperature=float(nominal_temperature), filter_cube=str(filter_cube), fluorescence_flatfield_lamp=fluorescence_flatfield_lamp, **other_metadata) if save_focus_stacks: try: # check if it's iterable for pos in save_focus_stacks: assert pos in named_positions except TypeError: num_to_save = int(save_focus_stacks) save_indices = numpy.random.permutation(len(named_positions))[:num_to_save] names = list(named_positions.keys()) save_focus_stacks = [names[i] for i in save_indices] metadata['save_focus_stacks'] = save_focus_stacks with (data_dir / 'experiment_metadata.json').open('w') as f: datafile.json_encode_legible_to_file(metadata, f)
def take_automated_plate_images(scope, out_dir): out_dir = pathlib.Path(out_dir) scope.camera.acquisition_sequencer.new_sequence() scope.camera.acquisition_sequencer.add_step(2, 'TL', tl_intensity=255) out_dir.mkdir(exist_ok=True) field_spacing = 2160 * 0.0065 * 1 / 2.5 # FILL ME IN WITH APPROPRIATE FIELD SIZE BASED ON 2.5X OBJECTIVE try: input('Specify center of plate') center_position = scope.stage.position input('Specify outer extent of plate') outer_position = scope.stage.position roi_radius = ((center_position[0] - outer_position[0])**2 + (center_position[1] - outer_position[1])**2)**0.5 # Define function to interpolate z - assume the plate surface is a parabola with radial symmetry about its center w.r.t. both x & y scale_param = (outer_position[2] - center_position[2]) / (roi_radius** 2) interpolate_z = lambda x, y: scale_param * ( (x - center_position[0])**2 + (y - center_position[1])**2) + center_position[2] grid_x = np.arange(center_position[0] - roi_radius / np.sqrt(2), center_position[0] + roi_radius / np.sqrt(2), field_spacing) grid_y = np.arange(center_position[1] - roi_radius / np.sqrt(2), center_position[1] + roi_radius / np.sqrt(2), field_spacing) xcoor, ycoor = np.meshgrid(grid_x, grid_y) xcoor = np.array( [pts if num % 2 else pts[::-1] for num, pts in enumerate(xcoor)] ) # invert the x-coordinates appropriately so that we make take min time to traverse the slide #raise Exception() pos_num = 0 for x, y in zip(xcoor.flatten(), ycoor.flatten()): scope.stage.position = [x, y, interpolate_z(x, y)] bf_image = scope.camera.acquisition_sequencer.run()[0] freeimage.write(bf_image, out_dir / f'_{pos_num:03d}.png') pos_num += 1 imaging_parameters = {'lamp': 'TL', 'exposure': 2, 'intensity': 255} with (out_dir / 'imaging_parameters.json').open('w') as param_file: datafile.json_encode_legible_to_file(imaging_parameters, param_file) scope.stage.position = center_position except KeyboardInterrupt: return
def reset_positions_manual(scope, experiment_dir, *annotation_filters, revert_z=False): '''Reset positions manually for an experiment (i.e. with a separate ris_widget window open) Parameters: scope - ScopeClient object as defined by scope.scope_client experiment_dir - str/pathlib.Path to experiment annotation_filters - Optional variable filters to use to isolate specific positions of interest Call with annotation filters like so: reset_position.reset_positions(scope, experiment_dir, elegant_filters.filter_excluded, elegant_filters.filter_live_animals) ''' experiment_dir = pathlib.Path(experiment_dir) print(f'Traversing {experiment_dir.name}') metadata = load_data.read_metadata(experiment_dir) if annotation_filters: experiment_annotations = load_data.read_annotations(experiment_dir) for filter in annotation_filters: experiment_annotations = load_data.filter_annotations( experiment_annotations, filter) positions = experiment_annotations.keys() else: positions = metadata['positions'].keys() new_positions = poll_positions(scope, metadata, positions, revert_z=revert_z) if new_positions: try: input(f'\nPress any key to save positions; ctrl-c to abort') time_label = time.strftime('%Y%m%d-%H%M-%S') with (experiment_dir / f'experiment_metadata_beforechangingpositions_{time_label}.json' ).open('w') as mdata_file: datafile.json_encode_legible_to_file(metadata, mdata_file) metadata['positions'].update(new_positions) load_data.write_metadata(metadata, experiment_dir) except KeyboardInterrupt: pass else: print('No positions found to reset')
def well_plate_acquisition(scope, out_dir, tl_intensity, grid_size=[4, 6], well_spacing_cc=12.92): ''' grid_size, well_spacing_cc for nominal Falcon 48-well plate (latter in mm) ''' out_dir = pathlib.Path(out_dir) scope.camera.acquisition_sequencer.new_sequence() scope.camera.acquisition_sequencer.add_step(2, 'TL', tl_intensity=tl_intensity) out_dir.mkdir() try: print('Specify xy-position for top-left well; press ctrl-c to abort.') input() topleft_position = scope.stage.position pos_num = 0 for row_num in range(grid_size[0]): for column_num in range(grid_size[1]): scope.stage.position = [ topleft_position[0] + column_num * well_spacing_cc, topleft_position[1] + row_num * well_spacing_cc, topleft_position[2], ] print('Adjust to desired z-position; press ctrl-c to abort.') input() bf_image = scope.camera.acquisition_sequencer.run()[0] freeimage.write(bf_image, out_dir / f'_{pos_num:03d}.png') pos_num += 1 imaging_parameters = { 'lamp': 'TL', 'exposure': 2, 'intensity': tl_intensity } with (out_dir / 'imaging_parameters.json').open('w') as param_file: datafile.json_encode_legible_to_file(imaging_parameters, param_file) except KeyboardInterrupt: return
def make_align_img(scope, expt_dir): expt_dir = pathlib.Path(expt_dir) try: scope_pos, my_image = take_img(scope, expt_dir) except KeyboardInterrupt: return time_label = time.strftime('%Y%m%d-%H%M-%S') with (expt_dir / 'experiment_metadata.json').open('r') as mdata_file: metadata = json.load(mdata_file) with (expt_dir / f'experiment_metadata_noalign_{time_label}.json' ).open('w') as mdata_file: datafile.json_encode_legible_to_file(metadata, mdata_file) metadata['align_position'] = scope_pos with (expt_dir / 'experiment_metadata.json').open('w') as mdata_file: datafile.json_encode_legible_to_file(metadata, mdata_file) (expt_dir / 'calibrations').mkdir(exist_ok=True) freeimage.write(my_image, expt_dir / 'calibrations' / 'align_image.png')
def _write(self, jobs): """Write Job tuples as json to self.backing_file.""" job_list = [[str(exec_file)] + rest for exec_file, *rest in jobs.values()] with self.jobs_lock, self.backing_file.open('w') as bf: datafile.json_encode_legible_to_file(job_list, bf)
def _write(self, jobs): """Write Job tuples as json to self.backing_file.""" job_list = [[str(exec_file)] + rest for exec_file, *rest in jobs.values()] with self.jobs_lock, self.backing_file.open('w') as bf: datafile.json_encode_legible_to_file(job_list, bf)
def perform_alignment(scope, expt_dir, rw): px_conversion = 1.304 / 1000 #mm/px expt_dir = pathlib.Path(expt_dir) before_img = freeimage.read( str(expt_dir / 'calibrations' / 'align_image.png')) with (expt_dir / 'experiment_metadata.json').open('r') as mdata_file: metadata = json.load(mdata_file) before_scope_pos = np.array(metadata['align_position']) if scope is not None: scope.stage.position = before_scope_pos # Set z to same position after_scope_pos, after_img = take_img(scope, expt_dir) else: # old debugging not on scope after_scope_pos = np.array([163.6972, 3.085, 23.739]) #~ after_img = freeimage.read('/mnt/scopearray/Sinha_Drew/testing/align_pics/after_img.png') #after_img = freeimage.read('/media/Data/Documents/Dropbox/align_pics/after_img.png') #after_img = freeimage.read('/home/zplab/Desktop/align_pics/after_img.png') if len(rw.flipbook.pages) > 0: rw.flipbook.pages.clear() rw.flipbook.pages.append(ImageList([Image(before_img), Image(after_img)])) #rw.layers[0].tint=[255,232,185,0.5] #rw.layers[1].tint = [270,70,255,0.5] # my_ptpicker = PointListPicker(rw.main_view,rw.main_scene.layer_stack_item) # my_ptpicker = SimplePointPicker(rw.main_view,rw.main_scene.layer_stack_item) my_ptpicker = point_set.PointSet(rw) #ip_input('Click on first landmark in old and new images; press Enter when done.') input( 'Click on first landmark in old and new images; press Enter when done.' ) before_first_pos, after_first_pos = [ np.array(point) for point in my_ptpicker.points ] # IN PIXELS print('Acquired first landmark') # my_ptpicker.points = [] for point in my_ptpicker.points: point.remove() ip_input( 'Click on second landmark in old and new images; press Enter when done.' ) before_next_pos, after_next_pos = [ np.array(point) for point in my_ptpicker.points ] print('Acquired second landmark') # my_ptpicker.points = [] for point in my_ptpicker.points: point.remove() xy_offset = after_scope_pos[:2] + after_first_pos * px_conversion - ( before_scope_pos[:2] + before_first_pos * px_conversion) xy_offset = xy_offset * [ -1, 1 ] #Correct for directionality of stage on ISCOPE CHECK THIS ON SCOPE!!!!!!!!!!! print(xy_offset) before_next_adj = before_next_pos - before_first_pos after_next_adj = after_next_pos - after_first_pos before_incline = np.arctan(before_next_adj[1] / before_next_adj[0]) after_incline = np.arctan(after_next_adj[1] / after_next_adj[0]) incline_offset = after_incline - before_incline print('Before incline:{:.2f}\nAfter incline:{:.2f}\nOffset:{:.2f}'.format( before_incline, after_incline, incline_offset)) # GOOD UP TO HERE. #raise Exception("xy:{} (px), incline:{} (deg)".format(xy_offset,incline_offset*360/2*np.pi)) old_metadata = metadata.copy() metadata['align_position'].append(after_scope_pos) # Save the old! for well in metadata['positions']: # Do rotation first rotated_point = np.dot( np.array([[np.cos(incline_offset), -np.sin(incline_offset)], [np.sin(incline_offset), np.cos(incline_offset)]]), np.array(metadata['positions'][well][:2] - np.array(before_scope_pos[:2] + before_first_pos * px_conversion)) ) + np.array(before_scope_pos[:2] + before_first_pos * px_conversion) metadata['positions'][well][:2] = list(rotated_point + xy_offset) wells = [ well_dir.parts[-1] for well_dir in expt_dir.iterdir() if str(well_dir.parts[-1]).isnumeric() ] metadata['realignment_time'] = metadata.get('realignment_time', []).append( time.strftime("%Y-%m-%dt%H%M")) # Compare before and after positions (offset only) scope.stage.position = list( np.append(before_scope_pos[:2] + xy_offset, before_scope_pos[2])) trash, final_landmark_img = take_img(scope, expt_dir, poll=False) rw.flipbook.pages.append( ImageList([Image(before_img), Image(final_landmark_img)])) ip_input( 'Press anything if landmarks looks aligned (xy-offset); ctrl-c to abort' ) # Compare first and last wells rw.flipbook.pages.clear() before_first_img = freeimage.read([ bf_file for bf_file in (expt_dir / wells[0]).iterdir() if 'bf.png' in bf_file.parts[-1] ][0]) scope.stage.position = list( np.append( np.array(old_metadata['positions'][wells[0]][:2]) + xy_offset, old_metadata['positions'][ wells[0]][2])) # Do one with the offset for debugging trash, after_first_offset_img = take_img(scope, expt_dir, poll=False) rw.flipbook.pages.append( ImageList([Image(before_first_img), Image(after_first_offset_img) ])) #,name='Before After Well'+str(wells[0])) scope.stage.position = metadata['positions'][wells[0]] trash, after_first_img = take_img(scope, expt_dir, poll=False) rw.flipbook.pages.append( ImageList([Image(before_first_img), Image(after_first_img) ])) #,name='Before After Well'+str(wells[0])) ip_input('Press anything if first well looks aligned; ctrl-c to abort') before_last_img = freeimage.read([ bf_file for bf_file in (expt_dir / wells[-1]).iterdir() if 'bf.png' in bf_file.parts[-1] ][0]) scope.stage.position = metadata['positions'][wells[-1]] trash, after_last_img = take_img(scope, expt_dir, poll=False) rw.flipbook.pages.clear() rw.flipbook.pages.append( ImageList([Image(before_last_img), Image(after_last_img) ])) # ,name='Before After Well'+str(wells[-1])) ip_input('Press anything if last well looks aligned; ctrl-c to abort') with (expt_dir / 'experiment_metadata_oldprealignment.json').open('w') as mdata_file: datafile.json_encode_legible_to_file(old_metadata, mdata_file) with (expt_dir / 'experiment_metadata.json').open('w') as mdata_file: datafile.json_encode_legible_to_file(metadata, mdata_file) freeimage.write(before_img, str(expt_dir / 'calibrations' / 'old_align_image.png')) freeimage.write(after_img, str(expt_dir / 'calibrations' / 'align_image.png'))