<commit_before>import subprocess
import configparser
from tempfile import NamedTemporaryFile
import os
from math import ceil
from shutil import move
import logging

# autofocus constants
DEFAULT_FOCUS_CRITERION = 0
DEFAULT_FOCUS_STEP_COARSE = 10.0
DEFAULT_FOCUS_STEP_FINE = 1.0

# NIS flags for exporting as multipage TIFF
EXPORT_TIFF_MASK_T = 1
EXPORT_TIFF_MASK_XY = 16
EXPORT_TIFF_MASK_Z = 256

# prefix for the named tiles in a multipoint ND-acq.
TILE_NAME_PREFIX = 'tile'

# export to TIFF needs a prefix -> dummy that is removed later
EXPORT_DUMMY_PREFIX = '$tiffexport$'

def export_nd2_to_tiff(path_to_nis, nd2_file, out_dir=None, combine_t=False, combine_yx=False, combine_z=True, combine_c=False):
    # NB: suffix order should be t, xy, z, c (?)

    if out_dir is None:
        # same dir
        out_dir = nd2_file.rsplit(os.sep, 1)[0]

    # multipage mask
    mask = 0
    if combine_t:
        mask += EXPORT_TIFF_MASK_T
    if combine_yx:
        mask += EXPORT_TIFF_MASK_XY
    if combine_z:
        mask += EXPORT_TIFF_MASK_Z

    try:
        ntf = NamedTemporaryFile(suffix='.mac', delete=False)
        cmd = '''
        char emptystring[1];
        ND_ExportToTIFF("{}","{}","{}",{},0,0,{});
        '''.format(nd2_file, out_dir, EXPORT_DUMMY_PREFIX, 3 if combine_c else 0, mask)

        ntf.writelines([bytes(cmd, 'utf-8')])
        ntf.close()
        subprocess.call(' '.join([quote(path_to_nis), '-mw', quote(ntf.name)]))
    finally:
        os.remove(ntf.name)

    # NIS needs a prefix for export -> manually remove the dummy prefix and rename files
    for f in os.listdir(out_dir):
        if f.startswith(EXPORT_DUMMY_PREFIX):
            move(os.path.join(out_dir, f), os.path.join(out_dir, f.replace(EXPORT_DUMMY_PREFIX, '')))


def gen_grid(fov, min_, max_, overlap, snake, half_fov_offset=True, center=True):
    """
    generate a grid of coordinates at which to do a tiled acquisition

    Parameters
    ----------
    fov: array-like
        field-of-view in units
    min_: array-like
        minimum of bbox to scan
    max_: array-like
        maximum of bbox to scan
    overlap: scalar \in (0,1)
        percent overlap
    snake: boolean
        whether to alternate in x or not
    half_fov_offset: boolean
        whether to correct for NIS 'centering' on locations (-> half FOV offset)
    center: boolean
        whether to center the grid on the bounding box or not (in this case, the object will be in the upper left corner)

    Returns
    -------
    grid: list of 2-tuples
        (x,y) - coordinates at which to image
    """

    # whether coordinates are increasing or decreasing in a dimension
    direction = [1 if max_[0] > min_[0] else -1, 1 if max_[1] > min_[1] else -1]

    # number of tiles
    tilesX = (abs(max_[0] - min_[0]) - fov[0]) / (fov[0] * (1 - overlap))
    tilesY = (abs(max_[1] - min_[1]) - fov[1]) / (fov[1] * (1 - overlap))
    tilesX = max(0, int(ceil(tilesX))) + 1
    tilesY = max(0, int(ceil(tilesY))) + 1

    # re-center grid on bbox
    if center:
        totalX = fov[0] + (tilesX - 1) * (fov[0] * (1 - overlap))
        totalY = fov[1] + (tilesY - 1) * (fov[1] * (1 - overlap))

        #print('{} {}'.format(totalX, totalY))
        extraX = totalX - abs(max_[0] - min_[0])
        extraY = totalY - abs(max_[1] - min_[1])

        #print('{} {}'.format(extraX, extraY))
        min_ = [min_[0] - 0.5 * extraX * direction[0], min_[1] - 0.5 * extraY * direction[1]]

    # correct for NIS's half FOV offset
    if half_fov_offset:
        min_ = [min_[0] + 0.5 * fov[0] * direction[0], min_[1] + 0.5 * fov[1] * direction[1]]

    # steps: increasing or decreasing
    stepX = fov[0] * (1 - overlap) if direction[0] == 1 else - (fov[0] * (1 - overlap))
    stepY = fov[1] * (1 - overlap) if direction[1] == 1 else - (fov[1] * (1 - overlap))

    res = []
    for y in range(tilesY):
        row = [(min_[0] + x * stepX, min_[1] + y * stepY) for x in range(tilesX)]
        if snake and (y % 2 != 0):
            row.reverse()
        res.extend(row)
        
    return res, tilesX, tilesY, overlap


def quote(s):
    return '"{}"'.format(s)


def do_autofocus(path_to_nis, step_coarse=None, step_fine=None, focus_criterion=None, focus_with_piezo=False):
    try:<commit_msg>added backup of optical configurations<commit_after>import subprocess
import configparser
from tempfile import NamedTemporaryFile
import os
from math import ceil
from shutil import move
import logging

# autofocus constants
DEFAULT_FOCUS_CRITERION = 0
DEFAULT_FOCUS_STEP_COARSE = 10.0
DEFAULT_FOCUS_STEP_FINE = 1.0

# NIS flags for exporting as multipage TIFF
EXPORT_TIFF_MASK_T = 1
EXPORT_TIFF_MASK_XY = 16
EXPORT_TIFF_MASK_Z = 256

# prefix for the named tiles in a multipoint ND-acq.
TILE_NAME_PREFIX = 'tile'

# export to TIFF needs a prefix -> dummy that is removed later
EXPORT_DUMMY_PREFIX = '$tiffexport$'

def export_nd2_to_tiff(path_to_nis, nd2_file, out_dir=None, combine_t=False, combine_yx=False, combine_z=True, combine_c=False):
    # NB: suffix order should be t, xy, z, c (?)

    if out_dir is None:
        # same dir
        out_dir = nd2_file.rsplit(os.sep, 1)[0]

    # multipage mask
    mask = 0
    if combine_t:
        mask += EXPORT_TIFF_MASK_T
    if combine_yx:
        mask += EXPORT_TIFF_MASK_XY
    if combine_z:
        mask += EXPORT_TIFF_MASK_Z

    try:
        ntf = NamedTemporaryFile(suffix='.mac', delete=False)
        cmd = '''
        char emptystring[1];
        ND_ExportToTIFF("{}","{}","{}",{},0,0,{});
        '''.format(nd2_file, out_dir, EXPORT_DUMMY_PREFIX, 3 if combine_c else 0, mask)

        ntf.writelines([bytes(cmd, 'utf-8')])
        ntf.close()
        subprocess.call(' '.join([quote(path_to_nis), '-mw', quote(ntf.name)]))
    finally:
        os.remove(ntf.name)

    # NIS needs a prefix for export -> manually remove the dummy prefix and rename files
    for f in os.listdir(out_dir):
        if f.startswith(EXPORT_DUMMY_PREFIX):
            move(os.path.join(out_dir, f), os.path.join(out_dir, f.replace(EXPORT_DUMMY_PREFIX, '')))


def gen_grid(fov, min_, max_, overlap, snake, half_fov_offset=True, center=True):
    """
    generate a grid of coordinates at which to do a tiled acquisition

    Parameters
    ----------
    fov: array-like
        field-of-view in units
    min_: array-like
        minimum of bbox to scan
    max_: array-like
        maximum of bbox to scan
    overlap: scalar \in (0,1)
        percent overlap
    snake: boolean
        whether to alternate in x or not
    half_fov_offset: boolean
        whether to correct for NIS 'centering' on locations (-> half FOV offset)
    center: boolean
        whether to center the grid on the bounding box or not (in this case, the object will be in the upper left corner)

    Returns
    -------
    grid: list of 2-tuples
        (x,y) - coordinates at which to image
    """

    # whether coordinates are increasing or decreasing in a dimension
    direction = [1 if max_[0] > min_[0] else -1, 1 if max_[1] > min_[1] else -1]

    # number of tiles
    tilesX = (abs(max_[0] - min_[0]) - fov[0]) / (fov[0] * (1 - overlap))
    tilesY = (abs(max_[1] - min_[1]) - fov[1]) / (fov[1] * (1 - overlap))
    tilesX = max(0, int(ceil(tilesX))) + 1
    tilesY = max(0, int(ceil(tilesY))) + 1

    # re-center grid on bbox
    if center:
        totalX = fov[0] + (tilesX - 1) * (fov[0] * (1 - overlap))
        totalY = fov[1] + (tilesY - 1) * (fov[1] * (1 - overlap))

        #print('{} {}'.format(totalX, totalY))
        extraX = totalX - abs(max_[0] - min_[0])
        extraY = totalY - abs(max_[1] - min_[1])

        #print('{} {}'.format(extraX, extraY))
        min_ = [min_[0] - 0.5 * extraX * direction[0], min_[1] - 0.5 * extraY * direction[1]]

    # correct for NIS's half FOV offset
    if half_fov_offset:
        min_ = [min_[0] + 0.5 * fov[0] * direction[0], min_[1] + 0.5 * fov[1] * direction[1]]

    # steps: increasing or decreasing
    stepX = fov[0] * (1 - overlap) if direction[0] == 1 else - (fov[0] * (1 - overlap))
    stepY = fov[1] * (1 - overlap) if direction[1] == 1 else - (fov[1] * (1 - overlap))

    res = []
    for y in range(tilesY):
        row = [(min_[0] + x * stepX, min_[1] + y * stepY) for x in range(tilesX)]
        if snake and (y % 2 != 0):
            row.reverse()
        res.extend(row)
        
    return res, tilesX, tilesY, overlap


def quote(s):
    return '"{}"'.format(s)


def backup_optical_configurations(path_to_nis, backup_path):
    """
    export all optical configurations as XML
    """
    try:
        ntf = NamedTemporaryFile(suffix='.mac', delete=False)
        cmd = '''BackupOptConf("{}");'''.format(backup_path)
        ntf.writelines([bytes(cmd, 'utf-8')])
        ntf.close()
        subprocess.call(' '.join([quote(path_to_nis), '-mw', quote(ntf.name)]))
    finally:
        os.remove(ntf.name)


def do_autofocus(path_to_nis, step_coarse=None, step_fine=None, focus_criterion=None, focus_with_piezo=False):
    try:
{'fraction_non_alphanumeric': 0.133970228838036, 'fraction_numerical': 0.021439680071095313, 'mean_word_length': 2.745008319467554, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2164295', 'n_tokens_mistral': 3313, 'n_tokens_neox': 3190, 'n_words': 895}