Source code for microscope_automation.orchestrator.write_zen_tiles_experiment

"""
Export position list for ZEN blue tiles experiment (.czsh)
Created on Jul 26, 2019

authors: Brian Kim, Calysta Yan, winfriedw
"""

import xml.etree.ElementTree as ET
import itertools
import string
import os
from microscope_automation.util import automation_messages_form_layout as message


[docs]class PositionWriter(object): """Converts positions to stage coordinates, saves positions to .czsh file Requirements: 1) Ensure the dummy_tile_positions.czsh path is specified in preferences under key "PathDummy" """ def __init__(self, zsd, plate, production_path, testing_mode=False): """ Initialization Input: zsd: String or integer Id of ZSD used for acquisition plate: String of integer Id of plate production_path: Path of the prouduction folder Output: none """ # Check Inputs if production_path is None: raise ValueError("Production path not specified") if type(plate) is not str: if type(plate) is int: plate = str(plate) else: raise ValueError("specified plate number is not a number or a string") if type(zsd) is not str: if type(zsd) is int: zsd = str(zsd) else: raise ValueError("specified ZSD id is not a number or a string") if isinstance(production_path, list): production_path = production_path[0] self.plate = plate self.path = os.path.join(production_path, plate, zsd) if not os.path.exists(self.path): raise OSError( "Please check zsd, plate, and production path" " info given to PositionWriter" ) self.zsd = zsd self.gen = self.iter_all_strings()
[docs] def convert_to_stage_coords( self, offset_x=0, offset_y=0, positions_list=[], header=False ): """Converts the distance of points from center of image to x-y coordinates in stage with 10 to 100x objective offsets. Input: offset_x: x_offset to account for in stage coordinate conversion offset_y: y_offset to accoutn for in stage coordinate conversion positions_list: center of well positions to convert to stage coordinates header: boolean of whether positions_list includes a header. Skips the first row if True. Default: False Output: converted_list: stage coordinates of positions converted from center of well positions """ # Check to ensure there are positions to convert if len(positions_list) <= 1: raise AssertionError( "positions list from automation software needs to contain coordinates" ) converted_list = [] obj_offset = [offset_x, offset_y] start = 1 if header else 0 for i in range(start, len(positions_list)): # positions_list[i][0] is name of position from automation software this_position = dict() this_position["name"] = positions_list[i][0] this_position["actual_x"] = ( positions_list[i][1] + obj_offset[0] ) # coordinate X + offset for X this_position["actual_y"] = ( positions_list[i][2] + obj_offset[1] ) # coordinate Y + offset Y this_position["actual_z"] = positions_list[i][ 3 ] # coordinate Z (no offset here) converted_list.append(this_position) return converted_list
[docs] def write(self, converted=[], dummy="", name_czsh="positions_output.czsh"): """ Writes coordinates to a dummy.czsh file, and saves it Input: converted: positions to write to the czsh file dummy: empty (no coordinates) .czsh file to use for writing name_czsh: name to save written .czsh file as Output: none """ # Where to write the positions to_write = os.path.join(self.path, name_czsh) to_append = self.get_next_pos_name() # append a letter to file name of .czsh split = name_czsh.split(".") split[len(split) - 2] += "_" + to_append to_write = os.path.join(self.path, ".".join(split)) # noqa # Theres no data here, or the file was mistakenly rewritten if len(converted) == 0: raise AssertionError( "Need positions to write, Did you call convert_to_stage_coords()?" ) # Get empty file try: tree = ET.parse(os.path.abspath(dummy)) except FileNotFoundError: dummy = message.read_string( "Dummy positions file " + dummy + " could not be found.", label="Enter a valid file path:", default="", ) tree = ET.parse(os.path.abspath(dummy)) root = tree.getroot() for single_tiles in root.iter("SingleTileRegions"): for n in converted: # Assign Values for writing tile = ET.SubElement(single_tiles, "SingleTileRegion") tile.set("Name", n["name"]) # Need str for tree.write() ET.SubElement(tile, "X").text = str(n["actual_x"]) ET.SubElement(tile, "Y").text = str(n["actual_y"]) ET.SubElement(tile, "Z").text = str(n["actual_z"]) ET.SubElement(tile, "IsUsedForAcquisition").text = "true" # Write Values tree.write(to_write)
[docs] def iter_all_strings(self): for size in itertools.count(1): for s in itertools.product(string.ascii_lowercase, repeat=size): yield "".join(s)
[docs] def label_gen(self): for s in self.gen: return s
[docs] def get_next_pos_name(self, test_mode=False, test_file_names=[]): """Find the next available letter label, but dont backfill files. Input: test_mode: False by default. Runs in test mode if True. test_file_names: list of test file names to use if running in test mode Output: label: unused alphabetical label for new positions list """ if test_mode: prefixed = test_file_names else: prefixed = [ filename for filename in os.listdir(self.path) if filename.startswith("positions_output_") ] # if there's no existing output files start with the leter 'a' if len(prefixed) == 0: return "a" # Split extension from name of file split = prefixed[len(prefixed) - 1].split(".") # split filename to get letter letter = split[len(split) - 2].split("_") # letters found letters = letter[len(letter) - 1] label = self.label_gen() while label != letters: label = self.label_gen() return self.label_gen()