question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature request: pyramid_gaussian() ported from scikit-image

See original GitHub issue
  • dask-image version: 0.2.0
  • Python version: 3.6.8
  • Operating System: macOS 10.15.5 Beta

Description

Right now I have a standard 2-D image, stored as a dask array. It’s very large, since it’s loaded from an SVS file, and I’d like to view it in napari. Since it’s so large, I’d like to take advantage of napari’s pyramid layers, which means I need to convert the array to a pyramidal form. skimage.transform.pyramid_gaussian can do this, but it eats up so much of my memory that the script gets SIGKILLed.

What I Did

with napari.gui_qt():
    da_img = # very large 2-D dask array
    napari.view_image(list(skimage.transform.pyramid_gaussian(da_img, multichannel=True)), name='image', is_pyramid=True)

and the script uses up all my memory and eventually gets SIGKILLed.

This might be because skimage.transform.pyramid_gaussian calls np.asarray. https://github.com/scikit-image/scikit-image/blob/ad15736162a216e46834cb4722914bfa01aeb3ae/skimage/transform/pyramids.py#L145-L224

Note that this is a feature request and not a bug report.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:1
  • Comments:12 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
GenevieveBuckleycommented, Jul 2, 2021

Here’s a rough sketch. We could do this by wrapping the same generator pattern around dask.array.blockwise

Example

import dask.array as da
import numpy as np
from skimage.transform import pyramid_reduce

def dask_pyramid_gaussian(image, max_layer=-1, downscale=2, sigma=None, order=1,
                        mode='reflect', cval=0, multichannel=False,
                        preserve_range=False, *, channel_axis=-1, **kwargs):
    layer = 0
    current_shape = image.shape

    prev_layer_image = image
    yield image

    # build downsampled images until max_layer is reached or downscale process
    # does not change image size
    while layer != max_layer:
        layer += 1

        layer_image = da.blockwise(pyramid_reduce, 'ij', prev_layer_image, 'ij',
                                   downscale=downscale, sigma=sigma, order=order, mode=mode, cval=cval,
                                   dtype=float, adjust_chunks={'i': lambda n: n / downscale, 'j': lambda n: n / downscale})

        prev_shape = np.asarray(prev_layer_image.shape)
        prev_layer_image = layer_image
        current_shape = np.asarray(layer_image.shape)

        # no change to previous pyramid layer
        if np.all(current_shape == prev_shape):
            break

        yield layer_image

And here’s a super tiny example of it in action. We could also use some of the Aperio example data to test it.

from skimage.data import camera

arr = da.from_array(camera(), chunks=(128, 128))
out = list(dask_pyramid_gaussian(arr))
out
[dask.array<array, shape=(512, 512), dtype=uint8, chunksize=(128, 128), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(256, 256), dtype=float64, chunksize=(64, 64), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(128, 128), dtype=float64, chunksize=(32, 32), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(64, 64), dtype=float64, chunksize=(16, 16), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(32, 32), dtype=float64, chunksize=(8, 8), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(16, 16), dtype=float64, chunksize=(4, 4), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(8, 8), dtype=float64, chunksize=(2, 2), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(4, 4), dtype=float64, chunksize=(1, 1), chunktype=numpy.ndarray>,
 dask.array<pyramid_reduce, shape=(0, 0), dtype=float64, chunksize=(0, 0), chunktype=numpy.ndarray>]

Limitations

There are a few key limitations here

  • it’s not n-dimensional (currently a 2D greyscale example)
  • still needs multichannel/channel_axis support, too
  • the smallest layer shape and chunk size is being reported as (0, 0), which is not correct (this happens when adjust_chunks tries to downsample an array that already has the smallest chunksize possible: (1,1).
  • performance considerations. One thing we may need to change is that as we reduce the image down, we might also want to have fewer chunks for those slices too. Lots of tiny chunks can impact performance (but also, so will a lot of rechunking calls). So we’ll need to be thoughtful and pick some good defaults.

Questions

We’ll need to test how good the baseline performance is with napari. Perhaps @sumanthratna or @jni could try this out.

Note: even if the performance is sluggish, there might still be a place for this. It’d be useful in situations where you have data, but don’t know which files are the interesting ones you plan to analyze later (so you don’t want to go to all the effort of processing ALL the files into a pyramidal format). Also, we can make sure it’s integrated well with workflows to save out a pyramidal file to disk (then you can do the initial exploration, decide it’s worthwhile saving as a pyramid for better performance, and easily continue with your work)

2reactions
VolkerHcommented, Jun 30, 2021

Just bumping this. This would be quite a useful dask image funcrionality to have when generating multiscale .ome.zarr/ngff files from very large arrays.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Build image pyramids — skimage v0.19.2 docs
The pyramid_gaussian function takes an image and yields successive images shrunk by a constant scale factor. Image pyramids are often used, e.g., to...
Read more >
pyrtools Documentation
Pyrtools is a python package for multi-scale image processing, adapted from ... Gaussian or Laplacian Pyramid), otherwise the column wrap is ...
Read more >
A Gaussian pyramid with scikit-image transform pyramid module
The Gaussian pyramid from an input image can be computed using the scikit-image.transform.pyramid module's pyramid_gaussian() function.
Read more >
scikit-image: doc/release/release_0.14.rst | Fossies
We're happy to announce the release of scikit-image v0.14.4! ... will receive bug fixes and backported features deemed important (by community demand) until ......
Read more >
SimpleITK Documentation - Read the Docs
of the pyramid to the next and the later receives the sigmas to use for smoothing when ... and superimpose it on top...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found