|
| 1 | +import os |
| 2 | +import gc |
| 3 | +import math |
| 4 | +import time |
| 5 | +import numpy as np |
| 6 | +from . import utils |
| 7 | +import nd2 |
| 8 | + |
| 9 | + |
| 10 | +def nd2_to_binary(ops): |
| 11 | + """finds nd2 files and writes them to binaries |
| 12 | +
|
| 13 | + Parameters |
| 14 | + ---------- |
| 15 | + ops: dictionary |
| 16 | + 'nplanes', 'data_path', 'save_path', 'save_folder', 'fast_disk', |
| 17 | + 'nchannels', 'keep_movie_raw', 'look_one_level_down' |
| 18 | +
|
| 19 | + Returns |
| 20 | + ------- |
| 21 | + ops : dictionary of first plane |
| 22 | + ops['reg_file'] or ops['raw_file'] is created binary |
| 23 | + assigns keys 'Ly', 'Lx', 'tiffreader', 'first_tiffs', |
| 24 | + 'nframes', 'meanImg', 'meanImg_chan2' |
| 25 | + """ |
| 26 | + |
| 27 | + t0 = time.time() |
| 28 | + # copy ops to list where each element is ops for each plane |
| 29 | + ops1 = utils.init_ops(ops) |
| 30 | + |
| 31 | + # open all binary files for writing |
| 32 | + # look for nd2s in all requested folders |
| 33 | + ops1, fs, reg_file, reg_file_chan2 = utils.find_files_open_binaries(ops1, False) |
| 34 | + ops = ops1[0] |
| 35 | + |
| 36 | + # loop over all nd2 files |
| 37 | + iall = 0 |
| 38 | + ik = 0 |
| 39 | + for file_name in fs: |
| 40 | + # open nd2 |
| 41 | + nd2_file = nd2.ND2File(file_name) |
| 42 | + nd2_dims = {k: i for i, k in enumerate(nd2_file.sizes)} |
| 43 | + |
| 44 | + valid_dimensions = "TZCYX" |
| 45 | + assert set(nd2_dims) <= set( |
| 46 | + valid_dimensions |
| 47 | + ), f"Unknown dimensions {set(nd2_dims)-set(valid_dimensions)} in file {file_name}." |
| 48 | + |
| 49 | + # Sort the dimensions in the order of TZCYX, skipping the missing ones. |
| 50 | + im = nd2_file.asarray().transpose( |
| 51 | + [nd2_dims[x] for x in valid_dimensions if x in nd2_dims] |
| 52 | + ) |
| 53 | + |
| 54 | + # Expand array to include the missing dimensions. |
| 55 | + for i, dim in enumerate("TZC"): |
| 56 | + if dim not in nd2_dims: |
| 57 | + im = np.expand_dims(im, i) |
| 58 | + |
| 59 | + nplanes = nd2_file.sizes["Z"] if "Z" in nd2_file.sizes else 1 |
| 60 | + nchannels = nd2_file.sizes["C"] if "C" in nd2_file.sizes else 1 |
| 61 | + nframes = nd2_file.sizes["T"] if "T" in nd2_file.sizes else 1 |
| 62 | + |
| 63 | + iblocks = np.arange(0, nframes, ops1[0]["batch_size"]) |
| 64 | + if iblocks[-1] < nframes: |
| 65 | + iblocks = np.append(iblocks, nframes) |
| 66 | + |
| 67 | + if nchannels > 1: |
| 68 | + nfunc = ops1[0]["functional_chan"] - 1 |
| 69 | + else: |
| 70 | + nfunc = 0 |
| 71 | + |
| 72 | + assert im.max() < 32768 and im.min() >= -32768, "image data is out of range" |
| 73 | + im = im.astype(np.int16) |
| 74 | + |
| 75 | + # loop over all frames |
| 76 | + for ichunk, onset in enumerate(iblocks[:-1]): |
| 77 | + offset = iblocks[ichunk + 1] |
| 78 | + im_p = np.array(im[onset:offset, :, :, :, :]) |
| 79 | + im2mean = im_p.mean(axis=0).astype(np.float32) / len(iblocks) |
| 80 | + for ichan in range(nchannels): |
| 81 | + nframes = im_p.shape[0] |
| 82 | + im2write = im_p[:, :, ichan, :, :] |
| 83 | + for j in range(0, nplanes): |
| 84 | + if iall == 0: |
| 85 | + ops1[j]["meanImg"] = np.zeros( |
| 86 | + (im_p.shape[3], im_p.shape[4]), np.float32 |
| 87 | + ) |
| 88 | + if nchannels > 1: |
| 89 | + ops1[j]["meanImg_chan2"] = np.zeros( |
| 90 | + (im_p.shape[3], im_p.shape[4]), np.float32 |
| 91 | + ) |
| 92 | + ops1[j]["nframes"] = 0 |
| 93 | + if ichan == nfunc: |
| 94 | + ops1[j]["meanImg"] += np.squeeze(im2mean[j, ichan, :, :]) |
| 95 | + reg_file[j].write( |
| 96 | + bytearray(im2write[:, j, :, :].astype("int16")) |
| 97 | + ) |
| 98 | + else: |
| 99 | + ops1[j]["meanImg_chan2"] += np.squeeze(im2mean[j, ichan, :, :]) |
| 100 | + reg_file_chan2[j].write( |
| 101 | + bytearray(im2write[:, j, :, :].astype("int16")) |
| 102 | + ) |
| 103 | + |
| 104 | + ops1[j]["nframes"] += im2write.shape[0] |
| 105 | + ik += nframes |
| 106 | + iall += nframes |
| 107 | + |
| 108 | + nd2_file.close() |
| 109 | + |
| 110 | + # write ops files |
| 111 | + do_registration = ops1[0]["do_registration"] |
| 112 | + for ops in ops1: |
| 113 | + ops["Ly"] = im.shape[3] |
| 114 | + ops["Lx"] = im.shape[4] |
| 115 | + if not do_registration: |
| 116 | + ops["yrange"] = np.array([0, ops["Ly"]]) |
| 117 | + ops["xrange"] = np.array([0, ops["Lx"]]) |
| 118 | + np.save(ops["ops_path"], ops) |
| 119 | + # close all binary files and write ops files |
| 120 | + for j in range(0, nplanes): |
| 121 | + reg_file[j].close() |
| 122 | + if nchannels > 1: |
| 123 | + reg_file_chan2[j].close() |
| 124 | + return ops1[0] |
0 commit comments