Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read images from cloud storage #84

Open
CMCDragonkai opened this issue Oct 8, 2018 · 8 comments
Open

Read images from cloud storage #84

CMCDragonkai opened this issue Oct 8, 2018 · 8 comments

Comments

@CMCDragonkai
Copy link

CMCDragonkai commented Oct 8, 2018

I was wondering what the relationship between this package and the function dask.array.image.imread that's already part of dask.

Especially as I detected that dask.array.image.imread doesn't actually make use of the remote data, so I couldn't give it a s3:// protocol.

@jakirkham
Copy link
Member

Sure. So this package provides a suite of image processing tools in the spirit of scipy.ndimage. Certainly loading image data into Dask is one piece of functionality it provides. Though most of the functionality is different types of image processing techniques that one may want to apply to the data once loaded into Dask Arrays (however one might choose to do that). It may be the case in the future that existing image utilities in Dask are relocated to or deprecated in favor of dask-image as was done with dask-ml, but we are not quite there yet.

To be more specific, dask-image includes a large variety of filters (e.g. smoothing, denoising, edge detection, Fourier, etc.), morphological operations, and some operations on label images. There is certainly room for this package to grow in these areas based on the needs of the community. The functionality here is designed specifically to handle the fact that not all data needed for an operation may be in the same chunk. So will add overlaps with other chunks for filters or pull out relevant pieces of different chunks when working with label images. Hopefully that clarifies what dask-image is trying to solve and how it differs from functionality in Dask.

Loading image data is generally a hard problem (even outside of Dask) due to the large variety of formats, image format extensions or specializations for specific fields, the requirements of different imaging modalities, file constraints (size, dimensionality, ordering, etc.), compression, access patterns, encryption, etc. As a consequence there are more than a few libraries that can be used to load image data with various tradeoffs that range between how closely the loaded data should match the format to smoothing out differences between many different formats by loading array data generally.

For imread specifically, dask-image has made some choices that are different from the imread function in Dask. These are done to improve graph construction performance. In either case, the actual data loading step is handed off to an external library. In Dask's case, this is scikit-image or a user defined function. In dask-image, this is PIMS, which then can use any of a number of different things including (but not limited to) scikit-image depending on what is installed and available to it. Both can be reasonable choices.

Would be happy to discuss the particular problem you are working with, but would need a bit more detail. Namely what image formats are involved, how the data is split up (if at all), whether authentication of some kind is needed, etc. There could be short term solutions using things like dask.delayed, S3fs, s3fs-fuse, etc., which would give you a way to load data that you need to analyze now. Longer term solutions would be integrating these things in a more appetizing way for users like yourself.

@jakirkham
Copy link
Member

Does that help? Any other questions or comments here?

@skeller88
Copy link

I have a related question. What's the recommended way to use dask to read .tiff images stored at gcs? I posted my question here: https://stackoverflow.com/questions/58422292/is-it-possible-to-read-a-tiff-file-from-a-remote-service-with-dask

@GenevieveBuckley
Copy link
Collaborator

So I don't really use google cloud storage, but here's where I'd start:

  1. Use gcfs to get the remote filenames (see https://gcsfs.readthedocs.io/en/latest/). Check this is working with the simple text file reading example they have.

  2. Try to read a single remote tiff file with a non-dask image library (maybe imageio, PIL, skimage, pims, whatever you use most often. For tiff images I think most or all of these will use Christoph Gohlke's tifffile to do the image reading). Does this bit work? If so, great, and I'd try it with dask next.

  3. Try loading gcs images with dask. John has written a blog post that might be helpful here: https://blog.dask.org/2019/06/20/load-image-data
    You can try:

>>> import dask_image
>>> x = dask_image.imread.imread('path/to/remote/location/*.tif')

which uses the pims library to read in images. I'm not really sure how well this plays with gcs, but if the first two steps above work this should work too.

OR

Or if what you're doing is a little more custom, you can take the same approach John does in the section "Lazily load images with Dask Array", which uses imageio.imread with dask-delayed to read data (and optionally joins them together with dask.array.block or dask.array.stack).

Finally, will you let me know how this goes? It's something that would be very helpful for other people to know too, so I'd like to add an example for this to the docs (or maybe see a post on it in the dask blog).

@skeller88
Copy link

skeller88 commented Oct 17, 2019 via email

@skeller88
Copy link

skeller88 commented Nov 15, 2019

Hi, I spun up a Kubernetes cluster and am running dask on it using the helm chart. I followed the dask blog post you shared as a template.

import time
import dask
import dask.array as da
import gcsfs
import imageio
import numpy as np
from distributed import Client

client = Client()

def read_filenames_from_gcs(filenames):
    bands = ["B02", "B03", "B04"]
    fs = gcsfs.GCSFileSystem(project='big_earth')

    def read(filename):
        imgs = []
        for band in bands:
            image_path = f"{filename}{filename.split('/')[-2]}_{band}.tif"
            r = fs.cat(image_path)
            imgs.append(imageio.core.asarray(imageio.imread(r, 'TIFF')))
        return np.stack(imgs, axis=-1).flatten()

    delayed_read = dask.delayed(read)
    # each image is 120 x 120, 3 bands total
    return [da.from_delayed(delayed_read(filename), shape=(14400 * 3, ), dtype=np.uint16) for filename in filenames]

fs = gcsfs.GCSFileSystem(project='big_earth')
filenames = fs.ls("big_earth/raw_rgb/tiff")

imgs = read_filenames_from_gcs(filenames)
imgs = da.stack(imgs, axis=0)

at this point the array dimensions are:
Screen Shot 2019-11-14 at 8 44 09 PM

so I rechunk the array:

imgs_rechunked = imgs.rechunk((50, 43200))

Screen Shot 2019-11-14 at 8 44 21 PM

then I attempt to persist the images to the cluster, but the jupyter notebook crashes:

imgs_rechunked = client.persist(imgs_rechunked)

See anything that I'm obviously doing wrong?

@mrocklin mrocklin changed the title Comparison with dask.array.image.imread Read images from cloud storage Jan 16, 2020
@GenevieveBuckley
Copy link
Collaborator

I'm sorry I completely missed your last message @skeller88

My best guess is that you just don't have enough RAM available to persist the whole array in memory. https://examples.dask.org/array.html#Persist-data-in-memory

There's also a memory leak issue with persist and dask-distributed, you can keep an eye on that conversation over dask/dask#2625

@skeller88
Copy link

skeller88 commented May 8, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants