MouseLand / suite2p

cell detection in calcium imaging recordings
http://www.suite2p.org
GNU General Public License v3.0
344 stars 240 forks source link

:bug: Improving the compatibility of saving TIF files #969

Closed Achuan-2 closed 1 year ago

Achuan-2 commented 1 year ago

I think the code for saving tiff can be modified to improve compatibility with other software The current issue is that the registration tifs exported by suite2p cannot be directly read by the denoising software DeepCad-RT just using tifffile.imread

from tifffile import imread, TiffFile, TiffWriter
import numpy as np

fname = 'temp_suie2p.tif'

# write a tiff file: suite2p methods
mov = imread(r"./file_00001_ch1.tif")
with TiffWriter(fname) as tif:
        for frame in np.floor(mov).astype(np.int16):
            tif.write(frame)

# read the tiff file: by imread
re_read = imread( 'temp_suie2p.tif')
print(re_read.shape)  # (512, 512)

# read the tiff file: by TiffFile
tif = TiffFile(fname)

image = tif.asarray()
print(image.shape) # (512, 512)

print(len(tif.series)) #  100
image = np.array([series.asarray() for series in tif.series])
print(image.shape) # (100, 512, 512)

print(len(tif.pages)) # 100
image = np.array([page.asarray() for page in tif.pages])
print(image.shape) # (100, 512, 512)

I think you can abandon the loop and write the whole mov directly by tif.write

from tifffile import imread, TiffFile, TiffWriter
import numpy as np

fname = 'temp_suie2p.tif'

# write a tiff file: modified from suite2p
mov = imread(r"./file_00001_ch1.tif")
with TiffWriter(fname) as tif:
        tif.write(np.floor(mov).astype(np.int16))

# read the tiff file: by imread
re_read = imread( 'temp_suie2p.tif')
print(re_read.shape)  # (100, 512, 512)

# read the tiff file: by TiffFile
tif =TiffFile(fname)

image = tif.asarray()
print(image.shape) # (100, 512, 512)

print(len(tif.series)) #  1
image = np.array([series.asarray() for series in tif.series])
print(image.shape) # (1, 100, 512, 512)

print(len(tif.pages)) # 100
image = np.array([page.asarray() for page in tif.pages])
print(image.shape) # (100, 512, 512)

That is because tifffile.imwrite writes a new series to the file. ref: https://github.com/cgohlke/tifffile/issues/104

claudioez commented 1 year ago

Dear Achuan, I am interested in denoising using DeepCad-RT previously movement-corrected data. Do you know how to transform the bin files to DeepCad-RT compatible tif?

carsen-stringer commented 1 year ago

@chriski777 has recently changed the tiff writing. Does this change here make sense @chriski777 ? (can suite2p process tiffs saved in this way?)

But Suite2p can run tiffs saved in the current way -- so I'm not sure why DeepCad-RT cannot use a more general syntax?

carsen-stringer commented 1 year ago

just as an aside, we have simple PCA denoising in suite2p that does not require a GPU or a black box neural network, you can turn that on with ops['denoise']=True and see if that helps. we do not generally recommend using a complicated algorithm for denoising unless you have some kind of ground truth to verify it's working well on your data.

Achuan-2 commented 1 year ago

@chriski777 has recently changed the tiff writing. Does this change here make sense @chriski777 ? (can suite2p process tiffs saved in this way?)

But Suite2p can run tiffs saved in the current way -- so I'm not sure why DeepCad-RT cannot use a more general syntax?

Please allow me to explain more clearly. In fact, tifffile support save mov as tiff

with TiffWriter(fname) as tif:
        tif.write(np.floor(mov).astype(np.int16))

but in your code, io/tiff.py/save_tiff, saving tiff frame by frame, which causes that each frame is a new series to the file. I don't think this method is a general syntax. This will result in the inability to simply read the generated tiff using imread directly

with TiffWriter(fname) as tif:
        for frame in np.floor(mov).astype(np.int16):
            tif.write(frame)

I think you can abandon the loop and write the whole mov directly by tif.write(mov)

def save_tiff(mov: np.ndarray, fname: str) -> None:
    """
    Save image stack array to tiff file.

    Parameters
    ----------
    mov: nImg x Ly x Lx
        The frames to save
    fname: str
        The tiff filename to save to

    """
    with TiffWriter(fname) as tif:
        tif.write(np.floor(mov).astype(np.int16))
chriski777 commented 1 year ago

Hi @Achuan-2, ah yep that's right. My initial reasoning for having the for loop was to accommodate very large files in which we don't want to read all the frames into memory at once. I've updated the code to use the following instead

with TiffWriter(fname) as tif:
        for frame in np.floor(mov).astype(np.int16):
            tif.write(frame, contiguous=True)

so now imread should work fine for any tiffs you create with the save_tiff. I'll close this PR but feel free to reopen if issues persist!

Achuan-2 commented 1 year ago

Hi @chriski777, I don't quite understand. Both methods(loop or directly imwrite all the frames) involve the variable mov that contains all frame data, so I think the use of memory should be the same. Please correct me if there is any mistakes.

chriski777 commented 1 year ago

@Achuan-2, sorry for being unclear. Yes, they should be the same for how it's used in save_tiff of io/tiff.py. What I was referring to is the write_tiff function in suite2p/io/binary.py. At least in BinaryFile, we use a memmap to accommodate huge files so I think these two approaches would be different in that context.

Achuan-2 commented 1 year ago

@chriski777 oh, I get it! But it seems that suite2p use io.save_tiff after registration, Is memmap used for other scenarios where TIF is saved?

chriski777 commented 1 year ago

Yep, that's right! The example colab notebook on the README shows some examples of how the binaryFile might be used for some of the module-specific wrapper functions.