Repository: napari/napari

Issue:
auto pyramid creation sometimes is not wanted
## 🐛 Bug

The auto pyramid creation that was enabled in #545 can sometimes lead to bad unexpected behavior - for example I just tried to pass a `100_000 x 100 x 100` zarr array which I will only ever want to view as a 2D timeseries and it started trying to compute a pyrmaid on that first dimension which it couldn't do.

I'm not sure how to both block this behavior, but then also allow a pyramid to be calculated if someone passes in a `20_000 x 20_000` image.

We could wait until we know what dimensions they actually want to view and so push this over to more of the view code, but I'm not so sure about that either.

## To Reproduce
run `examples/zarr_nD_image.py`


Discussion:

Resolving patch:
diff --git a/napari/layers/image/image.py b/napari/layers/image/image.py
--- a/napari/layers/image/image.py
+++ b/napari/layers/image/image.py
@@ -203,7 +203,9 @@ def data(self):
 
     @data.setter
     def data(self, data):
-        ndim, rgb, is_pyramid, data_pyramid = get_pyramid_and_rgb(data)
+        ndim, rgb, is_pyramid, data_pyramid = get_pyramid_and_rgb(
+            data, pyramid=self.is_pyramid, rgb=self.rgb
+        )
         self.is_pyramid = is_pyramid
         self.rgb = rgb
         self._data = data
diff --git a/napari/util/misc.py b/napari/util/misc.py
--- a/napari/util/misc.py
+++ b/napari/util/misc.py
@@ -113,8 +113,8 @@ def get_pyramid_and_rgb(data, pyramid=None, rgb=None):
     data : array or list
         Data to be checked if pyramid or if needs to be turned into a pyramid.
     pyramid : bool, optional
-        Value that can force data to be considered as a pyramid, otherwise
-        computed.
+        Value that can force data to be considered as a pyramid or not,
+        otherwise computed.
     rgb : bool, optional
         Value that can force data to be considered as a rgb, otherwise
         computed.
@@ -131,12 +131,9 @@ def get_pyramid_and_rgb(data, pyramid=None, rgb=None):
         If None then data is not and does not need to be a pyramid. Otherwise
         is a list of arrays where each array is a level of the pyramid.
     """
-
-    # Determine if pyramid
-    if pyramid is None:
-        pyramid = is_pyramid(data)
-
-    if pyramid:
+    # Determine if data currently is a pyramid
+    currently_pyramid = is_pyramid(data)
+    if currently_pyramid:
         init_shape = data[0].shape
     else:
         init_shape = data.shape
@@ -154,25 +151,36 @@ def get_pyramid_and_rgb(data, pyramid=None, rgb=None):
     else:
         ndim = len(init_shape)
 
-    if not pyramid:
-        # Guess if data should be pyramid
-        pyr_axes = should_be_pyramid(data.shape)
-        if np.any(pyr_axes):
-            pyramid = True
-            # Set axes to be downsampled to have a factor of 2
-            downscale = np.ones(len(data.shape))
-            downscale[pyr_axes] = 2
-            largest = np.min(np.array(data.shape)[pyr_axes])
-            # Determine number of downsample steps needed
-            max_layer = np.floor(np.log2(largest) - 9).astype(int)
-            data_pyramid = fast_pyramid(
-                data, downscale=downscale, max_layer=max_layer
+    if pyramid is False:
+        if currently_pyramid:
+            raise ValueError(
+                """Non pyramided data was requested, but pyramid
+                             data was passed"""
             )
-            data_pyramid = trim_pyramid(data_pyramid)
         else:
             data_pyramid = None
     else:
-        data_pyramid = trim_pyramid(data)
+        if currently_pyramid:
+            data_pyramid = trim_pyramid(data)
+            pyramid = True
+        else:
+            # Guess if data should be pyramid
+            pyr_axes = should_be_pyramid(data.shape)
+            if np.any(pyr_axes):
+                pyramid = True
+                # Set axes to be downsampled to have a factor of 2
+                downscale = np.ones(len(data.shape))
+                downscale[pyr_axes] = 2
+                largest = np.min(np.array(data.shape)[pyr_axes])
+                # Determine number of downsample steps needed
+                max_layer = np.floor(np.log2(largest) - 9).astype(int)
+                data_pyramid = fast_pyramid(
+                    data, downscale=downscale, max_layer=max_layer
+                )
+                data_pyramid = trim_pyramid(data_pyramid)
+            else:
+                data_pyramid = None
+                pyramid = False
 
     return ndim, rgb, pyramid, data_pyramid
 


Test patch:
diff --git a/napari/util/tests/test_misc.py b/napari/util/tests/test_misc.py
--- a/napari/util/tests/test_misc.py
+++ b/napari/util/tests/test_misc.py
@@ -166,6 +166,12 @@ def test_get_pyramid_and_rgb():
     assert not rgb
     assert ndim == 2
 
+    ndim, rgb, pyramid, data_pyramid = get_pyramid_and_rgb(data, pyramid=False)
+    assert not pyramid
+    assert data_pyramid is None
+    assert not rgb
+    assert ndim == 2
+
 
 def test_callsignature():
     # no arguments