Repository: napari/napari

Issue:
Attenuated MIP Initialization Error
## 🐛 Bug

The viewer is not programmatically updating the attenuation value for an 'attenuated_mip' rendering.  
Full discussion can be found here: https://forum.image.sc/t/attenuated-mip-refresh/39884/9

## To Reproduce

Steps to reproduce the behavior:

1.  Launch a simple script with a 3D image.
2. Try various attenuation values.
3. In general, it seems as if it is automatically showing an attenuation value of 0 regardless of what I provide in the viewer.add_image function.

`
import napari
import numpy as np
from tifffile import imread
import os

data_location = '/archive/bioinformatics/Danuser_lab/Fiolka/MicroscopeDevelopment/OPM/Drosophila/GAP43-mcherry/200703/Cell1'
membrane = imread(os.path.join(data_location, f'1_CH00_{1:06}.tif'))
membrane = np.array(membrane)

with napari.gui_qt():
    viewer = napari.Viewer(ndisplay=3)
    layer = viewer.add_image(data=membrane, rendering='attenuated_mip', attenuation=1)
`

## Expected behavior
I would expect the attenuation value, which is accurately reflected in the slider position to also be shown in the image.  

## Environment

 napari: 0.3.6.dev11+ge43f730
Platform: Linux-3.10.0-957.el7.x86_64-x86_64-with-redhat-7.6-Maipo
Python: 3.6.10 | packaged by conda-forge | (default, Apr 24 2020, 16:44:11) [GCC 7.3.0]
Qt: 5.14.2
PyQt5: 5.14.2
NumPy: 1.18.4
SciPy: 1.4.1
Dask: 2.17.2
VisPy: 0.6.4

GL version: 2.1 Mesa 18.3.4
MAX_TEXTURE_SIZE: 8192

Plugins:

napari-plugin-engine: 0.1.5
napari_volume
svg: 0.1.3

 - Linux OS

## Additional context
Nicholas Sofroniew identified the problem: 
...it comes from this line in vispy https://github.com/vispy/vispy/blob/caed4f0326dc9c0804b254339c4d89f66e6a01b6/vispy/visuals/volume.py#L598 and the fact that on startup the u_threshold is not in the shared_program. It seems to get added once things have appeared on screen, hence why things only start working when you move a slider.

I need to think about the best fix. It would be nice to fix on the vispy side, though presumably they had that line there for a reason. If we bypass that line by doing

self.node._threshold = float(attenuation)
self.node.shared_program['u_threshold'] = self.node._threshold
self.node.update()
ourselves inside here https://github.com/napari/napari/blob/e43f730170aab1e011c42060c65ba44133f6f18f/napari/_vispy/vispy_image_layer.py#L225 then it works fine. Curious what @talley thinks we should do. We can probably move this convo to github as it’s a pretty clear bug at this point.





Discussion:
@sofroniewn - I see that you have a pull request that will close this bug.  Any chance I would be able to test it out?  Thank you, Kevin.

Resolving patch:
diff --git a/napari/_vispy/vispy_image_layer.py b/napari/_vispy/vispy_image_layer.py
--- a/napari/_vispy/vispy_image_layer.py
+++ b/napari/_vispy/vispy_image_layer.py
@@ -138,6 +138,11 @@ def _on_threshold_change(self, event=None):
         elif rendering == Rendering.ATTENUATED_MIP:
             self.node.threshold = float(self.layer.attenuation)
 
+        # Fix for #1399, should be fixed in the VisPy threshold setter
+        if 'u_threshold' not in self.node.shared_program:
+            self.node.shared_program['u_threshold'] = self.node._threshold
+            self.node.update()
+
     def reset(self, event=None):
         self._reset_base()
         self._on_interpolation_change()


Test patch:
diff --git a/napari/_tests/test_with_screenshot.py b/napari/_tests/test_with_screenshot.py
new file mode 100644
--- /dev/null
+++ b/napari/_tests/test_with_screenshot.py
@@ -0,0 +1,31 @@
+import numpy as np
+import os
+import sys
+import pytest
+
+
+@pytest.mark.skipif(
+    sys.platform.startswith('win') or not os.getenv("CI"),
+    reason='Screenshot tests are not supported on napari windows CI.',
+)
+def test_changing_image_colormap(make_test_viewer):
+    """Test changing colormap changes rendering."""
+    data = np.zeros((100, 10, 10))
+    data[-1] = 1
+
+    viewer = make_test_viewer(show=True)
+    viewer.dims.ndisplay = 3
+    viewer.add_image(data, contrast_limits=[0, 1])
+    viewer.layers[0].rendering = 'attenuated_mip'
+
+    viewer.layers[0].attenuation = 2.0
+    screenshot = viewer.screenshot(canvas_only=True)
+    center = tuple(np.round(np.divide(screenshot.shape[:2], 2)).astype(int))
+    # Check that rendering has not been attenuated
+    assert screenshot[center + (0,)] > 200
+
+    viewer.layers[0].attenuation = 0.02
+    screenshot = viewer.screenshot(canvas_only=True)
+    center = tuple(np.round(np.divide(screenshot.shape[:2], 2)).astype(int))
+    # Check that rendering has been attenuated
+    assert screenshot[center + (0,)] < 60