<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://blog.dask.org</id>
  <title>Dask Working Notes - Posts tagged imaging</title>
  <updated>2026-03-05T15:05:26.407586+00:00</updated>
  <link href="https://blog.dask.org"/>
  <link href="https://blog.dask.org/blog/tag/imaging/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.12">ABlog</generator>
  <entry>
    <id>https://blog.dask.org/2021/05/07/skeleton-analysis/</id>
    <title>Skeleton analysis</title>
    <updated>2021-05-07T00:00:00+00:00</updated>
    <author>
      <name>Genevieve Buckley</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="executive-summary"&gt;

&lt;p&gt;In this blogpost, we show how to modify a skeleton network analysis with Dask to work with constrained RAM (eg: on your laptop). This makes it more accessible: it can run on a small laptop, instead of requiring access to a supercomputing cluster. Example code is also &lt;a class="reference external" href="https://github.com/GenevieveBuckley/distributed-skeleton-analysis/blob/main/distributed-skeleton-analysis-with-dask.ipynb"&gt;provided here&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 13)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="contents"&gt;
&lt;h1&gt;Contents&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#skeleton-structures-are-everywhere"&gt;&lt;span class="xref myst"&gt;Skeleton structures are everywhere&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#the-scientific-problem"&gt;&lt;span class="xref myst"&gt;The scientific problem&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#the-compute-problem"&gt;&lt;span class="xref myst"&gt;The compute problem&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#our-approach"&gt;&lt;span class="xref myst"&gt;Our approach&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#results"&gt;&lt;span class="xref myst"&gt;Results&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#limitations"&gt;&lt;span class="xref myst"&gt;Limitations&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#problems-encountered"&gt;&lt;span class="xref myst"&gt;Problems encountered&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#how-we-solved-them"&gt;&lt;span class="xref myst"&gt;How we solved them&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#problem-1-the-skeletonize-function-from-scikit-image-crashes-due-to-lack-of-ram"&gt;&lt;span class="xref myst"&gt;Problem 1: The skeletonize function from scikit-image crashes due to lack of RAM&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#problem-2-ragged-or-non-uniform-output-from-dask-array-chunks"&gt;&lt;span class="xref myst"&gt;Problem 2: Ragged or non-uniform output from Dask array chunks&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#problem-3-grabbing-the-image-chunks-with-an-overlap"&gt;&lt;span class="xref myst"&gt;Problem 3: Grabbing the image chunks with an overlap&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#problem-4-summary-statistics-with-skan"&gt;&lt;span class="xref myst"&gt;Problem 4: Summary statistics with skan&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#what's-next"&gt;&lt;span class="xref myst"&gt;What’s next&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#how-you-can-help"&gt;&lt;span class="xref myst"&gt;How you can help&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 30)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="skeleton-structures-are-everywhere"&gt;
&lt;h1&gt;Skeleton structures are everywhere&lt;/h1&gt;
&lt;p&gt;Lots of biological structures have a skeleton or network-like shape. We see these in all kinds of places, including:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;blood vessel branching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the branching of airways&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;neuron networks in the brain&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the root structure of plants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the capillaries in leaves&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;… and many more&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Analysing the structure of these skeletons can give us important information about the biology of that system.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 43)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="the-scientific-problem"&gt;
&lt;h1&gt;The scientific problem&lt;/h1&gt;
&lt;p&gt;For this bogpost, we will look at the blood vessels inside of a lung. This data was shared with us by &lt;a class="reference external" href="https://research.monash.edu/en/persons/marcus-kitchen"&gt;Marcus Kitchen&lt;/a&gt;, &lt;a class="reference external" href="https://hudson.org.au/researcher-profile/andrew-stainsby/"&gt;Andrew Stainsby&lt;/a&gt;, and their team of collaborators.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Skeleton network of blood vessels within a healthy lung" src="https://blog.dask.org/_images/skeleton-screenshot-crop.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;This research group focusses on lung development.
We want to compare the blood vessels in a healthy lung, against a lung from a hernia model. In the hernia model the lung is underdeveloped, squashed, and smaller.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 52)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="the-compute-problem"&gt;
&lt;h1&gt;The compute problem&lt;/h1&gt;
&lt;p&gt;These image volumes have a shape of roughtly 1000x1000x1000 pixels.
That doesn’t seem huge but given the high RAM consumption involved in processing the analysis, it crashes when running on a laptop.&lt;/p&gt;
&lt;p&gt;If you’re running out of RAM, there are two possible appoaches:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Get more RAM. Run things on a bigger computer, or move things to a supercomputing cluster. This has the advantage that you don’t need to rewrite your code, but it does require access to more powerful computer hardware.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manage the RAM you’ve got. Dask is good for this. If we use Dask, and some reasonable chunking of our arrays, we can manage things so that we never hit the RAM ceiling and crash. This has the advantage that you don’t need to buy more computer hardware, but it will require re-writing some code.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 63)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="our-approach"&gt;
&lt;h1&gt;Our approach&lt;/h1&gt;
&lt;p&gt;We took the second approach, using Dask so we can run our analysis on a small laptop with constrained RAM without crashing. This makes it more accessible, to more people.&lt;/p&gt;
&lt;p&gt;All the image pre-processing steps will be done with &lt;a class="reference external" href="http://image.dask.org/en/latest/"&gt;dask-image&lt;/a&gt;, and the &lt;a class="reference external" href="https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;skeletonize&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function of &lt;a class="reference external" href="https://scikit-image.org/"&gt;scikit-image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We use &lt;a class="reference external" href="https://jni.github.io/skan/"&gt;skan&lt;/a&gt; as the backbone of our analysis pipeline. &lt;a class="reference external" href="https://jni.github.io/skan/"&gt;skan&lt;/a&gt; is a library for skeleton image analysis. Given a skeleton image, it can describe statistics of the branches. To make it fast, the library is accelerated with &lt;a class="reference external" href="https://numba.pydata.org/"&gt;numba&lt;/a&gt; (if you’re curious, you can hear more about that in &lt;a class="reference external" href="https://www.youtube.com/watch?v=0pUPNMglnaE"&gt;this talk&lt;/a&gt; and its &lt;a class="reference external" href="https://github.com/jni/skan-talk-scipy-2019"&gt;related notebook&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;There is an example notebook containing the full details of the skeleton analysis &lt;a class="reference external" href="https://github.com/GenevieveBuckley/distributed-skeleton-analysis/blob/main/distributed-skeleton-analysis-with-dask.ipynb"&gt;available here&lt;/a&gt;. You can read on to hear just the highlights.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 73)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="results"&gt;
&lt;h1&gt;Results&lt;/h1&gt;
&lt;p&gt;The statistics from the blood vessel branches in the healthy and herniated lung shows clear differences between the two.&lt;/p&gt;
&lt;p&gt;Most striking is the difference in the number of blood vessel branches.
The herniated lung has less than 40% of the number of blood vessel branches in the healthy lung.&lt;/p&gt;
&lt;p&gt;There are also quantitative differences in the sizes of the blood vessels.
Here is a violin plot showing the distribution of the distances between the start and end points of each blood vessel branch. We can see that overall the blood vessel branches start and end closer together in the herniated lung. This is consistent with what we might expect, since the healthy lung is more well developed than the lung from the hernia model and the hernia has compressed that lung into a smaller overall space.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Violin plot comparing blood vessel thickness between a healthy and herniated lung" src="https://blog.dask.org/_images/compare-euclidean-distance.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;EDIT: This blogpost previously described the euclidean distance violin plot as measuring the thickness of the blood vessels. This is incorrect, and the mistake was not caught in the review process before publication. This post has been updated to correctly describe the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;euclidean-distance&lt;/span&gt;&lt;/code&gt; measuremet as the distance between the start and end of branches, as if you pulled a string taught between those points. An alternative measurement, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;branch-length&lt;/span&gt;&lt;/code&gt; describes the total branch length, including any winding twists and turns.&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 87)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="limitations"&gt;
&lt;h1&gt;Limitations&lt;/h1&gt;
&lt;p&gt;We rely on one big assumption: once skeletonized the reduced non-zero pixel data will fit into memory. While this holds true for datasets of this size (the cropped rabbit lung datasets are roughly 1000 x 1000 x 1000 pixels), it may not hold true for much larger data.&lt;/p&gt;
&lt;p&gt;Dask computation is also triggered at a few points through our prototype workflow. Ideally all computation would be delayed until the very final stage.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 93)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="problems-encountered"&gt;
&lt;h1&gt;Problems encountered&lt;/h1&gt;
&lt;p&gt;This project was originally intended to be a quick &amp;amp; easy one. Famous last words!&lt;/p&gt;
&lt;p&gt;What I wanted to do was to put the image data in a Dask array, and then use the &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function to do the image filtering, thresholding, skeletonizing, and skeleton analysis. What I soon found was that although the image filtering, thresholding, and skeletonization worked well, the skeleton analysis step had some problems:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Dask’s map_overlap function doesn’t handle ragged or non-uniformly shaped results from different image chunks very well, and…&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Internal function in the skan library were written in a way that was incompatible with distributed computation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 103)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-we-solved-them"&gt;
&lt;h1&gt;How we solved them&lt;/h1&gt;
&lt;section id="problem-1-the-skeletonize-function-from-scikit-image-crashes-due-to-lack-of-ram"&gt;
&lt;h2&gt;Problem 1: The skeletonize function from scikit-image crashes due to lack of RAM&lt;/h2&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;skeletonize&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function of &lt;a class="reference external" href="https://scikit-image.org/"&gt;scikit-image&lt;/a&gt; is very memory intensive, and was crashing on a laptop with 16GB RAM.&lt;/p&gt;
&lt;p&gt;We solved this by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Putting our image data into a Dask array with &lt;a class="reference external" href="http://image.dask.org/en/latest/dask_image.imread.html"&gt;dask-image &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;imread&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://docs.dask.org/en/latest/array-chunks.html?highlight=rechunk#rechunking"&gt;Rechunking&lt;/a&gt; the Dask array. We need to change the chunk shapes from 2D slices to small cuboid volumes, so the next step in the computation is efficient. We can choose the overall size of the chunks so that we can stay under the memory threshold needed for skeletonize.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, we run the &lt;a class="reference external" href="https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;skeletonize&lt;/span&gt;&lt;/code&gt; function&lt;/a&gt; on the Dask array chunks using the &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt; function&lt;/a&gt;. By limiting the size of the array chunks, we stay under our memory threshold!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="problem-2-ragged-or-non-uniform-output-from-dask-array-chunks"&gt;
&lt;h2&gt;Problem 2: Ragged or non-uniform output from Dask array chunks&lt;/h2&gt;
&lt;p&gt;The skeleton analysis functions will return results with ragged or non-uniform length for each image chunk. This is unsurpising, because different chunks will have different numbers of non-zero pixels in our skeleton shape.&lt;/p&gt;
&lt;p&gt;When working with Dask arrays, there are two very commonly used functions: &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html#dask.array.map_blocks"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; and &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;. Here’s what happens when we try a function with ragged outputs with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; versus &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# our dummy analysis function&lt;/span&gt;
    &lt;span class="n"&gt;random_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt;, everything works well:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;drop_axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# this works well&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;But if we need some overlap for function &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;foo&lt;/span&gt;&lt;/code&gt; to work correctly, then we run into problems:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;drop_axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# incorrect results&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Here, the first and last element of the results from foo are trimmed off before the results are concatenated, which we don’t want! Setting the keyword argument &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;trim=False&lt;/span&gt;&lt;/code&gt; would help avoid this problem, except then we get an error:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;drop_axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# ValueError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately for us, it’s really important to have a 1 pixel overlap in our array chunks, so that we can tell if a skeleton branch is ending or continuing on into the next chunk.&lt;/p&gt;
&lt;p&gt;There’s some complexity in the way &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt; results are concatenated back together so rather than diving into that, a more straightforward solution is to use &lt;a class="reference external" href="https://docs.dask.org/en/latest/delayed.html"&gt;Dask delayed&lt;/a&gt; instead. &lt;a class="reference external" href="https://github.com/chrisroat"&gt;Chris Roat&lt;/a&gt; shows a nice example of how we can use &lt;a class="reference external" href="https://docs.dask.org/en/latest/delayed.html"&gt;Dask delayed&lt;/a&gt; in a list comprehension that is then concatenated with Dask (&lt;a class="reference external" href="https://github.com/dask/dask/issues/7589"&gt;link to original discussion&lt;/a&gt;).&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.dataframe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dd&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nd"&gt;@dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delayed&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Make each dataframe a different size&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                         &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

&lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_meta&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_delayed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ravel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# no overlap&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ddf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ddf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; It’s very important to pass in a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/code&gt; keyword argument to the function &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;from_delayed&lt;/span&gt;&lt;/code&gt;. Without it, things will be extremely inefficient!&lt;/p&gt;
&lt;p&gt;If the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/code&gt; keyword argument is not given, Dask will try and work out what it should be. Ordinarily that might be a good thing, but inside a list comprehension that means those tasks are computed slowly and sequentially before the main computation even begins, which is horribly inefficient. Since we know ahead of time what kinds of results we expect from our analysis function (we just don’t know the length of each set of results), we can use the &lt;a class="reference external" href="https://docs.dask.org/en/latest/dataframe-api.html#dask.dataframe.utils.make_meta"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;utils.make_meta&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function to help us here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="problem-3-grabbing-the-image-chunks-with-an-overlap"&gt;
&lt;h2&gt;Problem 3: Grabbing the image chunks with an overlap&lt;/h2&gt;
&lt;p&gt;Now that we’re using &lt;a class="reference external" href="https://docs.dask.org/en/latest/delayed.html"&gt;Dask delayed&lt;/a&gt; to piece together our skeleton analysis results, it’s up to us to handle the array chunks overlap ourselves.&lt;/p&gt;
&lt;p&gt;We’ll do that by modifying Dask’s &lt;a class="reference external" href="https://github.com/dask/dask/blob/21aaf44d4d25bdba05951b85f3f2d943b823e82d/dask/array/core.py#L209-L225"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.array.core.slices_from_chunks&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function, into something that will be able to handle an overlap. Some special handling is required at the boundaries of the Dask array, so that we don’t try to slice past the edge of the array.&lt;/p&gt;
&lt;p&gt;Here’s what that looks like (&lt;a class="reference external" href="https://gist.github.com/GenevieveBuckley/decd23c22ee3417f7d78e87f791bc081"&gt;gist&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itertools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array.slicing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cached_cumsum&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;slices_from_chunks_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;array_shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cumdims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cached_cumsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_zero&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bds&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;slices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shapes&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cumdims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;inner_slices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxshape&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shapes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;array_shape&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;slice_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
            &lt;span class="n"&gt;slice_stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;slice_start&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;slice_start&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;slice_stop&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;maxshape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;slice_stop&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
            &lt;span class="n"&gt;inner_slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slice_stop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner_slices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now that we can slice an image chunk plus an extra pixel of overlap, all we need is a way to do that for all the chunks in an array. Drawing inspiration from this &lt;a class="reference external" href="https://github.com/dask/dask-image/blob/63543bf2f6553a8150f45289492bf614e1945ac0/dask_image/ndmeasure/__init__.py#L299-L303"&gt;block iteration&lt;/a&gt; we make a similar iterator.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;block_iter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndindex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;numblocks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getitem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;slices_from_chunks_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_meta&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;row&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;col&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;intermediate_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skeleton_graph_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;block_iter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intermediate_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_duplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# we need to drop duplicates because it counts pixels in the overlapping region twice&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With these results, we’re able to create the sparse skeleton graph.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="problem-4-summary-statistics-with-skan"&gt;
&lt;h2&gt;Problem 4: Summary statistics with skan&lt;/h2&gt;
&lt;p&gt;Skeleton branch statistics can be calculate with the &lt;a class="reference external" href="https://jni.github.io/skan/api/skan.csr.html#skan.csr.summarize"&gt;skan &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;summarize&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function. The problem here is that the function expects a &lt;a class="reference external" href="https://jni.github.io/skan/api/skan.csr.html#skan.csr.Skeleton"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Skeleton&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; object instance, but initializing a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Skeleton&lt;/span&gt;&lt;/code&gt; object calls methods that are not compatible for distributed analysis.&lt;/p&gt;
&lt;p&gt;We’ll solve this problem by first initializing a &lt;a class="reference external" href="https://jni.github.io/skan/api/skan.csr.html#skan.csr.Skeleton"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Skeleton&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; object instance with a tiny dummy dataset, then overwriting the attributes of the skeleton object with our real results. This is a hack, but it lets us achieve our goal: summary branch statistics for our large dataset.&lt;/p&gt;
&lt;p&gt;First we make a Skeleton object instance with dummy data:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;skan._testdata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;skeleton0&lt;/span&gt;

&lt;span class="n"&gt;skeleton_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Skeleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skeleton0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# initialize with dummy data&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then we overwrite the attributes with the previously calculated results:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;skeleton_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skeleton_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;skeleton_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;skeleton_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;
&lt;span class="n"&gt;skeleton_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;degrees&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;skeleton_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then finally we can calculate the summary branch statistics:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;skan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;summarize&lt;/span&gt;

&lt;span class="n"&gt;statistics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skel_obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;statistics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head text-right"&gt;&lt;p&gt;&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;skeleton-id&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;node-id-src&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;node-id-dst&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;branch-distance&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;branch-type&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;mean-pixel-value&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;stdev-pixel-value&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;euclidean-distance&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-right"&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.474584&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.00262514&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;595&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;596&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;595&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;596&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;9&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;8.19615&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.464662&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.00299629&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;37&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;622&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;43&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;392&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;590&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;37&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;400&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;622&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;43&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;392&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;590&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;33.5261&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;10&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;11&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.483393&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.00771038&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;49&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;391&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;589&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;50&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;391&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;589&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;49&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;391&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;589&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;50&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;391&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;589&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-right"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;13&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;19&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6.82843&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.464325&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0139064&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;52&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;389&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;588&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;55&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;385&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;588&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;52&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;389&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;588&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;55&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;385&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;588&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-right"&gt;&lt;p&gt;4&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;7&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;21&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;23&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.45862&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0104024&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;57&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;382&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;587&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;58&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;380&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;586&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;57&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;382&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;587&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;58&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;380&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;586&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2.44949&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;statistics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head text-left"&gt;&lt;p&gt;&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;skeleton-id&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;node-id-src&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;node-id-dst&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;branch-distance&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;branch-type&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;mean-pixel-value&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;stdev-pixel-value&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-src-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;image-coord-dst-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-src-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-0&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-1&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;coord-dst-2&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-right"&gt;&lt;p&gt;euclidean-distance&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-left"&gt;&lt;p&gt;count&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1095&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-left"&gt;&lt;p&gt;mean&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2089.38&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;11520.1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;11608.6&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22.9079&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2.00091&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.663422&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0418607&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;591.939&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;430.303&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;377.409&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;594.325&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;436.596&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;373.419&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;591.939&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;430.303&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;377.409&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;594.325&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;436.596&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;373.419&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;190.13&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-left"&gt;&lt;p&gt;std&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;636.377&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6057.61&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6061.18&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;24.2646&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0302199&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.242828&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0559064&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;174.04&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;194.499&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;97.0219&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;173.353&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;188.708&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;96.8276&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;174.04&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;194.499&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;97.0219&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;173.353&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;188.708&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;96.8276&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;151.171&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-left"&gt;&lt;p&gt;min&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.414659&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6.79493e-06&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;39&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;116&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;39&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;114&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;39&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;116&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;22&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;39&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;114&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-left"&gt;&lt;p&gt;25%&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1586&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6215.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;6429.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1.73205&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.482&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.00710439&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;468.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;278.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;313&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;475&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;299.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;307&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;468.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;278.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;313&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;475&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;299.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;307&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;72.6946&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-left"&gt;&lt;p&gt;50%&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2431&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;11977&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;12010&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;16.6814&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.552626&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0189069&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;626&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;405&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;388&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;627&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;410&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;381&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;626&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;405&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;388&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;627&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;410&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;381&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;161.059&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-left"&gt;&lt;p&gt;75%&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2542.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;16526.5&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;16583&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;35.0433&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;2&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.768359&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.0528814&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;732&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;579&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;434&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;734&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;590&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;432&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;732&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;579&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;434&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;734&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;590&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;432&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;265.948&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-left"&gt;&lt;p&gt;max&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;8034&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;26820&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;26822&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;197.147&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;3&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;1.29687&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;0.357193&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;976&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;833&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;622&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;976&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;841&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;606&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;976&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;833&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;622&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;976&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;841&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;606&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-right"&gt;&lt;p&gt;737.835&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Success!&lt;/p&gt;
&lt;p&gt;We’ve achieved distributed skeleton analysis with Dask.
You can see the example notebook containing the full details of the skeleton analysis &lt;a class="reference external" href="https://github.com/GenevieveBuckley/distributed-skeleton-analysis/blob/main/distributed-skeleton-analysis-with-dask.ipynb"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 294)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="what-s-next"&gt;
&lt;h1&gt;What’s next?&lt;/h1&gt;
&lt;p&gt;A good next step is modifing the &lt;a class="reference external" href="https://github.com/jni/skan"&gt;skan&lt;/a&gt; library code so that it directly supports distributed skeleton analysis.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/05/07/skeleton-analysis.md&lt;/span&gt;, line 298)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-you-can-help"&gt;
&lt;h1&gt;How you can help&lt;/h1&gt;
&lt;p&gt;If you’d like to get involved, there are a couple of options:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Try a similar analysis on your own data. The notebook with the full example code is &lt;a class="reference external" href="https://github.com/GenevieveBuckley/distributed-skeleton-analysis/blob/main/distributed-skeleton-analysis-with-dask.ipynb"&gt;available here&lt;/a&gt;. You can share or ask questions in the &lt;a class="reference external" href="https://join.slack.com/t/dask/shared_invite/zt-mfmh7quc-nIrXL6ocgiUH2haLYA914g"&gt;Dask slack&lt;/a&gt; or &lt;a class="reference internal" href="#twitter.com/dask_dev"&gt;&lt;span class="xref myst"&gt;on twitter&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Help add support for distributed skeleton analysis to skan. Head on over to the &lt;a class="reference external" href="https://github.com/jni/skan/issues/"&gt;skan issues page&lt;/a&gt; and leave a comment if you’d like to join in.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2021/05/07/skeleton-analysis/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="imaging" label="imaging"/>
    <category term="lifescience" label="life science"/>
    <category term="skan" label="skan"/>
    <category term="skeletonanalysis" label="skeleton analysis"/>
    <published>2021-05-07T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2021/03/29/apply-pretrained-pytorch-model/</id>
    <title>Dask with PyTorch for large scale image analysis</title>
    <updated>2021-03-29T00:00:00+00:00</updated>
    <author>
      <name>Genevieve Buckley</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/29/apply-pretrained-pytorch-model.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="executive-summary"&gt;

&lt;p&gt;This post explores applying a pre-trained &lt;a class="reference external" href="https://pytorch.org/"&gt;PyTorch&lt;/a&gt; model in parallel with Dask Array.&lt;/p&gt;
&lt;p&gt;We cover a simple example applying a pre-trained UNet to a stack of images to generate features for every pixel.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/29/apply-pretrained-pytorch-model.md&lt;/span&gt;, line 15)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="a-worked-example"&gt;
&lt;h1&gt;A Worked Example&lt;/h1&gt;
&lt;p&gt;Let’s start with an example applying a pre-trained &lt;a class="reference external" href="https://arxiv.org/abs/1505.04597"&gt;UNet&lt;/a&gt; to a stack of light sheet microscopy data.&lt;/p&gt;
&lt;p&gt;In this example, we:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Load the image data from Zarr into a multi-chunked Dask array&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load a pre-trained PyTorch model that featurizes images&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Construct a function to apply the model onto each chunk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply that function across the Dask array with the dask.array.map_blocks function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store the result back into Zarr format&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="step-1-load-the-image-data"&gt;
&lt;h2&gt;Step 1. Load the image data&lt;/h2&gt;
&lt;p&gt;First, we load the image data into a Dask array.&lt;/p&gt;
&lt;p&gt;The example dataset we’re using here is lattice lightsheet microscopy of the tail region of a zebrafish embryo. It is described in &lt;a class="reference external" href="http://dx.doi.org/10.1126/science.aaq1392"&gt;this Science paper&lt;/a&gt; (see Figure 4), and provided with permission from Srigokul Upadhyayula.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Liu &lt;em&gt;et al.&lt;/em&gt; 2018 “Observing the cell in its native state: Imaging subcellular dynamics in multicellular organisms” &lt;em&gt;Science&lt;/em&gt;, Vol. 360, Issue 6386, eaaq1392 DOI: 10.1126/science.aaq1392 (&lt;a class="reference external" href="http://dx.doi.org/10.1126/science.aaq1392"&gt;link&lt;/a&gt;)&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;This is the same data that we analysed in our last &lt;a class="reference external" href="https://blog.dask.org/2019/08/09/image-itk"&gt;blogpost on Dask and ITK&lt;/a&gt;. You should note the similarities to that workflow even though we are now using new libaries and performing different analyses.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/Users/nicholassofroniew/Github/image-demos/data/LLSM&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Load our data&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_zarr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AOLLSM_m4_560nm.zarr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;zarr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="step-2-load-a-pre-trained-pytorch-model"&gt;
&lt;h2&gt;Step 2. Load a pre-trained PyTorch model&lt;/h2&gt;
&lt;p&gt;Next, we load our pre-trained UNet model.&lt;/p&gt;
&lt;p&gt;This UNet model takes in an 2D image and returns a 2D x 16 array, where each pixel is now associate with a feature vector of length 16.&lt;/p&gt;
&lt;p&gt;We thank Mars Huang for training this particular UNet on a corpous of biological images to produce biologically relevant feature vectors, as part of his work on &lt;a class="reference external" href="https://github.com/transformify-plugins/segmentify"&gt;interactive bio-image segmentation&lt;/a&gt;. These features can then be used for more downstream image processing tasks such as image segmentation.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Load our pretrained UNet¶&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;segmentify.model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UNet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load_unet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Load a pretrained UNet model.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="c1"&gt;# load in saved model&lt;/span&gt;
    &lt;span class="n"&gt;pth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;model_args&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;model_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;model_state&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UNet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;model_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_state_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# remove last layer and activation&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;segment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_unet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;HPA_3.pth&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="step-3-construct-a-function-to-apply-the-model-to-each-chunk"&gt;
&lt;h2&gt;Step 3. Construct a function to apply the model to each chunk&lt;/h2&gt;
&lt;p&gt;We make a function to apply our pre-trained UNet model to each chunk of the Dask array.&lt;/p&gt;
&lt;p&gt;Because Dask arrays are just made out of Numpy arrays which are easily converted to Torch arrays, we’re able to leverage the power of machine learning at scale.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Apply UNet featurization&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;unet_featurize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Featurize pixels in an image using pretrained UNet model.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;torch&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract the 2D image data from the Dask array&lt;/span&gt;
    &lt;span class="c1"&gt;# Original Dask array dimensions were (time, z-slice, y, x)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Put the data into a shape PyTorch expects&lt;/span&gt;
    &lt;span class="c1"&gt;# Expected dimensions are (Batch x Channel x Width x Height)&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# convert image to torch Tensor&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# pass image through model&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# generate feature vectors (w,h,f)&lt;/span&gt;
    &lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Add back the leading length-one dimensions&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note: Very observant readers might notice that the steps for extracting the 2D image data and then putting it into a shape PyTorch expects appear to be redundant. It is redundant for our particular example, but that might easily not have been the case.&lt;/p&gt;
&lt;p&gt;To explain this in more detail, the UNet expects 4D input, with dimensions &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;(Batch&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;Channel&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;Width&lt;/span&gt; &lt;span class="pre"&gt;x&lt;/span&gt; &lt;span class="pre"&gt;Height)&lt;/span&gt;&lt;/code&gt;. The original Dask array dimensions were &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;(time,&lt;/span&gt; &lt;span class="pre"&gt;z-slice,&lt;/span&gt; &lt;span class="pre"&gt;y,&lt;/span&gt; &lt;span class="pre"&gt;x)&lt;/span&gt;&lt;/code&gt;. In our example it just so happens those match in a way that makes removing and then adding the leading dimensions redundant, but depending on the shape of the original Dask array this might not have been true.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="step-4-apply-that-function-across-the-dask-array"&gt;
&lt;h2&gt;Step 4. Apply that function across the Dask array&lt;/h2&gt;
&lt;p&gt;Now we apply that function to the data in our Dask array using &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html?highlight=map_blocks#dask.array.map_blocks"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.array.map_blocks&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Apply UNet featurization&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unet_featurize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;new_axis&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;unet_featurize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="step-5-store-the-result-back-into-zarr-format"&gt;
&lt;h2&gt;Step 5. Store the result back into Zarr format&lt;/h2&gt;
&lt;p&gt;Last, we store the result from the UNet model featurization as a zarr array.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Trigger computation and store&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_zarr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AOLLSM_featurized.zarr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overwrite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now we’ve saved our output, these features can be used for more downstream image processing tasks such as image segmentation.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="summing-up"&gt;
&lt;h2&gt;Summing up&lt;/h2&gt;
&lt;p&gt;Here we’ve shown how to apply a pre-trained PyTorch model to a Dask array of image data.&lt;/p&gt;
&lt;p&gt;Because our Dask array chunks are Numpy arrays, they can be easily converted to Torch arrays. This way, we’re able to leverage the power of machine learning at scale.&lt;/p&gt;
&lt;p&gt;This workflow was very similar to &lt;a class="reference external" href="https://blog.dask.org/2019/08/09/image-itk"&gt;our example&lt;/a&gt; using the dask.array.map_blocks function with ITK to perform image deconvolution. This shows you can easily adapt the same type of workflow to achieve many different types of analysis with Dask.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2021/03/29/apply-pretrained-pytorch-model/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="PyTorch" label="PyTorch"/>
    <category term="deeplearning" label="deep learning"/>
    <category term="imaging" label="imaging"/>
    <published>2021-03-29T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2021/03/19/image-segmentation/</id>
    <title>Image segmentation with Dask</title>
    <updated>2021-03-19T00:00:00+00:00</updated>
    <author>
      <name>Genevieve Buckley</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="executive-summary"&gt;

&lt;p&gt;We look at how to create a basic image segmentation pipeline, using the &lt;a class="reference external" href="http://image.dask.org/en/latest/"&gt;dask-image&lt;/a&gt; library.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 13)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="contents"&gt;
&lt;h1&gt;Contents&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#just-show-me-the-code"&gt;&lt;span class="xref myst"&gt;Just show me the code&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#image-segmentation-pipeline"&gt;&lt;span class="xref myst"&gt;Image segmentation pipeline&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#set-up-your-python-environment"&gt;&lt;span class="xref myst"&gt;Set up your python environment&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#download-the-example-data"&gt;&lt;span class="xref myst"&gt;Download the example data&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#step-1-reading-in-data"&gt;&lt;span class="xref myst"&gt;Step 1: Reading in data&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#step-2-siltering-images"&gt;&lt;span class="xref myst"&gt;Step 2: Filtering images&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#step-3-segmenting-objects"&gt;&lt;span class="xref myst"&gt;Step 3: Segmenting objects&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#step-4-morphological-operations"&gt;&lt;span class="xref myst"&gt;Step 4: Morphological operations&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#step-5-measuring-objects"&gt;&lt;span class="xref myst"&gt;Step 5: Measuring objects&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#custom-functions"&gt;&lt;span class="xref myst"&gt;Custom functions&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#dask-map_overlap-and-map_blocks"&gt;&lt;span class="xref myst"&gt;Dask map_overlap and map_blocks&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#dask-delayed"&gt;&lt;span class="xref myst"&gt;Dask delayed decorator&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#scikit-image-apply_parallel-function"&gt;&lt;span class="xref myst"&gt;scikit-image apply_parallel&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#scaling-up-computation"&gt;&lt;span class="xref myst"&gt;Scaling up computation&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#bonus-content-using-arrays-on-gpu"&gt;&lt;span class="xref myst"&gt;Bonus content: using arrays on GPU&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#how-you-can-get-involved"&gt;&lt;span class="xref myst"&gt;How you can get involved&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The content of this blog post originally appeared as &lt;a class="reference external" href="https://github.com/genevieveBuckley/dask-image-talk-2020"&gt;a conference talk in 2020&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 34)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="just-show-me-the-code"&gt;
&lt;h1&gt;Just show me the code&lt;/h1&gt;
&lt;p&gt;If you want to run this yourself, you’ll need to download the example data from the Broad Bioimage Benchmark Collection: https://bbbc.broadinstitute.org/BBBC039&lt;/p&gt;
&lt;p&gt;And install these requirements:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.4.0&lt;/span&gt; &lt;span class="n"&gt;tifffile&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Here’s our full pipeline:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image.imread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ndmorph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data/BBBC039/images/*.tif&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaussian_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigma&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;thresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threshold_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blocksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;threshold_images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresh&lt;/span&gt;
&lt;span class="n"&gt;structuring_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]])&lt;/span&gt;
&lt;span class="n"&gt;binary_images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmorph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binary_closing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;structuring_element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;mean_intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can keep reading for a step by step walkthrough of this image segmentation pipeline, or you can skip ahead to the sections on &lt;a class="reference internal" href="#Custom-functions"&gt;&lt;span class="xref myst"&gt;custom functions&lt;/span&gt;&lt;/a&gt;, &lt;a class="reference internal" href="#Scaling-up-computation"&gt;&lt;span class="xref myst"&gt;scaling up computation&lt;/span&gt;&lt;/a&gt;, or &lt;a class="reference internal" href="#Bonus-content:-using-arrays-on-GPU"&gt;&lt;span class="xref myst"&gt;GPU acceleration&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 65)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="image-segmentation-pipeline"&gt;
&lt;h1&gt;Image segmentation pipeline&lt;/h1&gt;
&lt;p&gt;Our basic image segmentation pipeline has five steps:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Reading in data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filtering images&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Segmenting objects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Morphological operations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring objects&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="set-up-your-python-environment"&gt;
&lt;h2&gt;Set up your python environment&lt;/h2&gt;
&lt;p&gt;Before we begin, we’ll need to set up our python virtual environment.&lt;/p&gt;
&lt;p&gt;At a minimum, you’ll need:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.4.0&lt;/span&gt; &lt;span class="n"&gt;tifffile&lt;/span&gt; &lt;span class="n"&gt;matplotlib&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Optionally, you can also install the &lt;a class="reference external" href="https://napari.org/"&gt;napari&lt;/a&gt; image viewer to visualize the image segmentation.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;napari[all]&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To use napari from IPython or jupyter, run the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;%gui&lt;/span&gt; &lt;span class="pre"&gt;qt&lt;/span&gt;&lt;/code&gt; magic in a cell before calling napari. See the &lt;a class="reference external" href="https://napari.org/"&gt;napari documentation&lt;/a&gt; for more details.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="download-the-example-data"&gt;
&lt;h2&gt;Download the example data&lt;/h2&gt;
&lt;p&gt;We’ll use the publically available image dataset &lt;a class="reference external" href="https://bbbc.broadinstitute.org/BBBC039"&gt;BBBC039&lt;/a&gt; Caicedo et al. 2018, available from the Broad Bioimage Benchmark Collection &lt;a class="reference external" href="http://dx.doi.org/10.1038/nmeth.2083"&gt;Ljosa et al., Nature Methods, 2012&lt;/a&gt;. You can download the dataset here: &lt;a class="reference external" href="https://bbbc.broadinstitute.org/BBBC039"&gt;https://bbbc.broadinstitute.org/BBBC039&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Example image from the BBBC039 dataset, Broad Bioimage Benchmark Collection" src="https://blog.dask.org/_images/BBBC039-example-image.png" /&gt;&lt;/p&gt;
&lt;p&gt;These are fluorescence microscopy images, where we see the nuclei in individual cells.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="step-1-reading-in-data"&gt;
&lt;h2&gt;Step 1: Reading in data&lt;/h2&gt;
&lt;p&gt;Step one in our image segmentation pipeline is to read in the image data. We can do that with the &lt;a class="reference external" href="http://image.dask.org/en/latest/dask_image.imread.html"&gt;dask-image imread function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We pass the path to the folder full of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;*.tif&lt;/span&gt;&lt;/code&gt; images from our example data.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image.imread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data/BBBC039/images/*.tif&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="HTML reprsentation of a Dask array" src="https://blog.dask.org/_images/dask-array-html-repr.png" /&gt;&lt;/p&gt;
&lt;p&gt;By default, each individual &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.tif&lt;/span&gt;&lt;/code&gt; file on disk has become one chunk in our Dask array.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="step-2-filtering-images"&gt;
&lt;h2&gt;Step 2: Filtering images&lt;/h2&gt;
&lt;p&gt;Denoising images with a small amount of blur can improve segmentation later on. This is a common first step in a lot of image segmentation pipelines. We can do this with the dask-image &lt;a class="reference external" href="http://image.dask.org/en/latest/dask_image.ndfilters.html#dask_image.ndfilters.gaussian_filter"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gaussian_filter&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;

&lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaussian_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigma&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="step-3-segmenting-objects"&gt;
&lt;h2&gt;Step 3: Segmenting objects&lt;/h2&gt;
&lt;p&gt;Next, we want to separate the objects in our images from the background. There are lots of different ways we could do this. Because we have fluorescent microscopy images, we’ll use a thresholding method.&lt;/p&gt;
&lt;section id="absolute-threshold"&gt;
&lt;h3&gt;Absolute threshold&lt;/h3&gt;
&lt;p&gt;We could set an absolute threshold value, where we’d consider pixels with intensity values below this threshold to be part of the background.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;absolute_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let’s have a look at these images with the napari image viewer. First we’ll need to use the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;%gui&lt;/span&gt; &lt;span class="pre"&gt;qt&lt;/span&gt;&lt;/code&gt; magic:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="n"&gt;qt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And now we can look a the images with napari:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;napari&lt;/span&gt;

&lt;span class="n"&gt;viewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Viewer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;viewer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;absolute_threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;viewer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contrast_limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;img src="/images/2021-image-segmentation/napari-absolute-threshold.png" alt="Absolute threshold napari screenshot" width="700" height="476"&gt;
&lt;p&gt;But there’s a problem here.&lt;/p&gt;
&lt;p&gt;When we look at the results for different image frames, it becomes clear that there is no “one size fits all” we can use for an absolute threshold value. Some images in the dataset have quite bright backgrounds, others have fluorescent nuclei with low brightness. We’ll have to try a different kind of thresholding method.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="local-threshold"&gt;
&lt;h3&gt;Local threshold&lt;/h3&gt;
&lt;p&gt;We can improve the segmentation using a local thresholding method.&lt;/p&gt;
&lt;p&gt;If we calculate a threshold value independently for each image frame then we can avoid the problem caused by fluctuating background intensity between frames.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;thresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndfilters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threshold_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;threshold_images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresh&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Let&amp;#39;s take a look at the images with napari&lt;/span&gt;
&lt;span class="n"&gt;viewer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold_images&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;img src="/images/2021-image-segmentation/napari-local-threshold.png" alt="Local threshold napari screenshot" width="700" height="476"&gt;
&lt;p&gt;The results here look much better, this is a much cleaner separation of nuclei from the background and it looks good for all the image frames.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="step-4-morphological-operations"&gt;
&lt;h2&gt;Step 4: Morphological operations&lt;/h2&gt;
&lt;p&gt;Now that we have a binary mask from our threshold, we can clean it up a bit with some morphological operations.&lt;/p&gt;
&lt;p&gt;Morphological operations are changes we make to the shape of structures a binary image. We’ll briefly describe some of the basic concepts here, but for a more detailed reference you can look at &lt;a class="reference external" href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html"&gt;this excellent page of the OpenCV documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Erosion&lt;/strong&gt; is an operation where the edges of structures in a binary image are eaten away, or eroded.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Example: Erosion of a binary image" src="https://blog.dask.org/_images/erosion.png" /&gt;&lt;/p&gt;
&lt;p&gt;Image credit: &lt;a class="reference external" href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html"&gt;OpenCV documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dilation&lt;/strong&gt; is the opposite of an erosion. With dilation, the edges of structures in a binary image are expanded.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Example: Dilation of a binary image" src="https://blog.dask.org/_images/dilation.png" /&gt;&lt;/p&gt;
&lt;p&gt;Image credit: &lt;a class="reference external" href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html"&gt;OpenCV documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We can combine morphological operations in different ways to get useful effects.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;morphological opening&lt;/strong&gt; operation is an erosion, followed by a dilation.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Example: Morphological opening of a binary image" src="https://blog.dask.org/_images/opening.png" /&gt;&lt;/p&gt;
&lt;p&gt;Image credit: &lt;a class="reference external" href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html"&gt;OpenCV documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the example image above, we can see the left hand side has a noisy, speckled background. If the structuring element used for the morphological operations is larger than the size of the noisy speckles, they will disappear completely in the first erosion step. Then when it is time to do the second dilation step, there’s nothing left of the noise in the background to dilate. So we have removed the noise in the background, while the major structures we are interested in (in this example, the J shape) are restored almost perfectly.&lt;/p&gt;
&lt;p&gt;Let’s use this morphological opening trick to clean up the binary images in our segmentation pipeline.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndmorph&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;structuring_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]])&lt;/span&gt;
&lt;span class="n"&gt;binary_images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmorph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binary_opening&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;structure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;structuring_element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You’ll notice here that we need to be a little bit careful about the structuring element. All our image frames are combined in a single Dask array, but we only want to apply the morphological operation independently to each frame.
To do this, we sandwich the default 2D structuring element between two layers of zeros. This means the neighbouring image frames have no effect on the result.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Default 2D structuring element&lt;/span&gt;

&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="step-5-measuring-objects"&gt;
&lt;h2&gt;Step 5: Measuring objects&lt;/h2&gt;
&lt;p&gt;The last step in any image processing pipeline is to make some kind of measurement. We’ll turn our binary mask into a label image, and then measure the intensity and size of those objects.&lt;/p&gt;
&lt;p&gt;For the sake of keeping the computation time in this tutorial nice and quick, we’ll measure only a small subset of the data. Let’s measure all the objects in the first three image frames (roughly 300 nuclei).&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;

&lt;span class="c1"&gt;# Create labelled mask&lt;/span&gt;
&lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_images&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;structuring_element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_features&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# [1, 2, 3, ...num_features]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Here’s a screenshot of the label image generated from our mask.&lt;/p&gt;
&lt;img src="/images/2021-image-segmentation/napari-label-image.png" alt="Label image napari screenshot" width="700" height="476"&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Number of nuclei:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="go"&gt;Number of nuclei: 271&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;section id="measure-objects-in-images"&gt;
&lt;h3&gt;Measure objects in images&lt;/h3&gt;
&lt;p&gt;The dask-image &lt;a class="reference external" href="http://image.dask.org/en/latest/dask_image.ndmeasure.html"&gt;ndmeasure subpackage&lt;/a&gt; includes a number of different measurement functions. In this example, we’ll choose to measure:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;The area in pixels of each object, and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The average intensity of each object.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;mean_intensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndmeasure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;label_images&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="run-computation-and-plot-results"&gt;
&lt;h3&gt;Run computation and plot results&lt;/h3&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;plt&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean_intensity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gca&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Area vs mean intensity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Area (pixels)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Mean intensity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Matplotlib graph of dask-image measurement results: " src="https://blog.dask.org/_images/dask-image-matplotlib-output.png" /&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 285)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="custom-functions"&gt;
&lt;h1&gt;Custom functions&lt;/h1&gt;
&lt;p&gt;What if you want to do something that isn’t included in the dask-image API? There are several options we can use to write custom functions.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;dask &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html?highlight=map_overlap#dask.array.map_overlap"&gt;map_overlap&lt;/a&gt; / &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html?highlight=map_blocks#dask.array.map_blocks"&gt;map_blocks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dask &lt;a class="reference external" href="https://docs.dask.org/en/latest/delayed.html"&gt;delayed&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;scikit-image &lt;a class="reference external" href="https://scikit-image.org/docs/dev/api/skimage.util.html#skimage.util.apply_parallel"&gt;apply_parallel()&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="dask-map-overlap-and-map-blocks"&gt;
&lt;h2&gt;Dask map_overlap and map_blocks&lt;/h2&gt;
&lt;p&gt;The Dask array &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html#dask.array.map_overlap"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; and &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html#dask.array.map_blocks"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; are what is used to build most of the functions in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-image&lt;/span&gt;&lt;/code&gt;. You can use them yourself too. They will apply a function to each chunk in a Dask array.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;my_custom_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ... does something really cool&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_custom_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_dask_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can read more about &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-overlap.html"&gt;overlapping computations here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-delayed"&gt;
&lt;h2&gt;Dask delayed&lt;/h2&gt;
&lt;p&gt;If you want more flexibility and fine-grained control over your computation, then you can use &lt;a class="reference external" href="https://docs.dask.org/en/latest/delayed.html"&gt;Dask delayed&lt;/a&gt;. You can get started &lt;a class="reference external" href="https://tutorial.dask.org/01_dask.delayed.html"&gt;with the Dask delayed tutorial here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="scikit-image-apply-parallel-function"&gt;
&lt;h2&gt;scikit-image apply_parallel function&lt;/h2&gt;
&lt;p&gt;If you’re a person who does a lot of image processing in python, one tool you’re likely to already be using is &lt;a class="reference external" href="https://scikit-image.org/"&gt;scikit-image&lt;/a&gt;. I’d like to draw your attention to the &lt;a class="reference external" href="https://scikit-image.org/docs/dev/api/skimage.util.html?highlight=apply_parallel#skimage.util.apply_parallel"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;apply_parallel&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function available in scikit-image. It uses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map-overlap&lt;/span&gt;&lt;/code&gt;, and can be very helpful.&lt;/p&gt;
&lt;p&gt;It’s useful not only when when you have big data, but also in cases where your data fits into memory but the computation you want to apply to the data is memory intensive. This might cause you to exceed the available RAM, and &lt;a class="reference external" href="https://scikit-image.org/docs/dev/api/skimage.util.html?highlight=apply_parallel#skimage.util.apply_parallel"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;apply_parallel&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; is great for these situations too.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 318)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="scaling-up-computation"&gt;
&lt;h1&gt;Scaling up computation&lt;/h1&gt;
&lt;p&gt;When you want to scale up from a laptop onto a supercomputing cluster, you can use &lt;a class="reference external" href="https://distributed.dask.org/en/latest/"&gt;dask-distributed&lt;/a&gt; to handle that.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="c1"&gt;# Setup a local cluster&lt;/span&gt;
&lt;span class="c1"&gt;# By default this sets up 1 worker per core&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See the &lt;a class="reference external" href="https://distributed.dask.org/en/latest/"&gt;documentation here&lt;/a&gt; to get set up for your system.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 334)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="bonus-content-using-arrays-on-gpu"&gt;
&lt;h1&gt;Bonus content: using arrays on GPU&lt;/h1&gt;
&lt;p&gt;We’ve recently been adding GPU support to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-image&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We’re able to add GPU support using a library called &lt;a class="reference external" href="https://cupy.dev/"&gt;CuPy&lt;/a&gt;. &lt;a class="reference external" href="https://cupy.dev/"&gt;CuPy&lt;/a&gt; is an array library with a numpy-like API, accelerated with NVIDIA CUDA. Instead of having Dask arrays which contain numpy chunks, we can have Dask arrays containing cupy chunks instead. This &lt;a class="reference external" href="https://blog.dask.org/2019/01/03/dask-array-gpus-first-steps"&gt;blogpost&lt;/a&gt; explains the benefits of GPU acceleration and gives some benchmarks for computations on CPU, a single GPU, and multiple GPUs.&lt;/p&gt;
&lt;section id="gpu-support-available-in-dask-image"&gt;
&lt;h2&gt;GPU support available in dask-image&lt;/h2&gt;
&lt;p&gt;From &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-image&lt;/span&gt;&lt;/code&gt; version 0.6.0, there is GPU array support for four of the six subpackages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;imread&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ndfilters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ndinterp&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ndmorph&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Subpackages of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-image&lt;/span&gt;&lt;/code&gt; that do &lt;em&gt;not&lt;/em&gt; yet have GPU support are.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;ndfourier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ndmeasure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We hope to add GPU support to these in the future.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="an-example"&gt;
&lt;h2&gt;An example&lt;/h2&gt;
&lt;p&gt;Here’s an example of an image convolution with Dask on the CPU:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# CPU example&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image.ndfilters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convolve&lt;/span&gt;

&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;convolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And here’s the same example of an image convolution with Dask on the GPU. The only thing necessary to change is the type of input arrays.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Same example moved to the GPU&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cupy&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- import cupy instead of numpy (version &amp;gt;=7.7.0)&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image.ndfilters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convolve&lt;/span&gt;

&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cupy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cupy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cupy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- cupy dask array&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cupy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;- cupy array&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;convolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can’t mix arrays on the CPU and arrays on the GPU in the same computation. This is why the weights &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;w&lt;/span&gt;&lt;/code&gt; must be a cupy array in the second example above.&lt;/p&gt;
&lt;p&gt;Additionally, you can transfer data between the CPU and GPU. So in situations where the GPU speedup is larger than than cost associated with transferring data, this may be useful to do.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="reading-in-images-onto-the-gpu"&gt;
&lt;h2&gt;Reading in images onto the GPU&lt;/h2&gt;
&lt;p&gt;Generally, we want to start our image processing by reading in data from images stored on disk. We can use the &lt;a class="reference external" href="http://image.dask.org/en/latest/dask_image.imread.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;imread&lt;/span&gt;&lt;/code&gt;&lt;/a&gt; function with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;arraytype=cupy&lt;/span&gt;&lt;/code&gt; keyword argument to do this.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_image.imread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;

&lt;span class="n"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data/BBBC039/images/*.tif&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;images_on_gpu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data/BBBC039/images/*.tif&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arraytype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cupy&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/19/image-segmentation.md&lt;/span&gt;, line 404)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="how-you-can-get-involved"&gt;
&lt;h1&gt;How you can get involved&lt;/h1&gt;
&lt;p&gt;Create and share your own segmentation or image processing workflows with Dask (&lt;a class="reference external" href="https://github.com/dask/dask-blog/issues/47"&gt;join the current discussion on segmentation&lt;/a&gt; or &lt;a class="reference external" href="https://github.com/dask/dask-blog/issues/new?assignees=&amp;amp;amp;labels=%5B%22type%2Ffeature%22%2C+%22needs-triage%22%5D&amp;amp;amp;template=feature-request.md"&gt;propose a new blogpost topic here&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Contribute to adding GPU support to dask-image: https://github.com/dask/dask-image/issues/133&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2021/03/19/image-segmentation/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="imaging" label="imaging"/>
    <published>2021-03-19T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2021/03/04/the-life-science-community/</id>
    <title>Getting to know the life science community</title>
    <updated>2021-03-04T00:00:00+00:00</updated>
    <author>
      <name>Genevieve Buckley</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="executive-summary"&gt;

&lt;p&gt;Dask wants to better support the needs of life scientists. We’ve been getting to know the community, in order to better understand:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Who is out there?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What kind of problems are they trying to solve?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’ve learned that:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Lots of people want more examples tailored to their specific scientifc domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better integration of Dask into other software is considered very important.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing memory constraints when working with big data is a common pain point.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our strategic plan for this year involves three parallel streams:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#infrastructure"&gt;&lt;span class="xref myst"&gt;INFRASTRUCTURE&lt;/span&gt;&lt;/a&gt; (60%) - improvements to Dask, or to other software with many life science users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#outreach"&gt;&lt;span class="xref myst"&gt;OUTREACH&lt;/span&gt;&lt;/a&gt; (20%) - blogposts, talks, webinars, tutorials, and examples.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#applications"&gt;&lt;span class="xref myst"&gt;APPLICATIONS&lt;/span&gt;&lt;/a&gt; (20%) - the application of Dask to a specific life science problem, collaborating with individual labs or groups.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you still want to have your say, it’s not too late -
&lt;a class="reference external" href="https://t.co/0NeknSdrO9?amp=1"&gt;click this link to get in touch!&lt;/a&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 31)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="contents"&gt;
&lt;h1&gt;Contents&lt;/h1&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#background"&gt;&lt;span class="xref myst"&gt;Background&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#what-we-learned"&gt;&lt;span class="xref myst"&gt;What we learned&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#from-dask-users"&gt;&lt;span class="xref myst"&gt;From Dask users&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#from-other-software-libraries"&gt;&lt;span class="xref myst"&gt;From other software libraries&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#opportunities-we-see"&gt;&lt;span class="xref myst"&gt;Opportunities we see&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#strategic-plan"&gt;&lt;span class="xref myst"&gt;Strategic plan&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#limitations"&gt;&lt;span class="xref myst"&gt;Limitations&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#methods"&gt;&lt;span class="xref myst"&gt;Methods&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 42)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="background"&gt;
&lt;h1&gt;Background&lt;/h1&gt;
&lt;p&gt;Recently Dask &lt;a class="reference external" href="https://chanzuckerberg.com/eoss/proposals/"&gt;won some funding&lt;/a&gt; to hire a developer (&lt;a class="reference external" href="https://github.com/GenevieveBuckley/"&gt;Genevieve Buckley&lt;/a&gt;) to improve Dask specifically for life sciences.&lt;/p&gt;
&lt;p&gt;Working with scientists is a really great way to drive growth in open source projects. Both scientists and software developers benefit. Early on, Dask had a lot of success integrating with the geosciences community. It’d be great to see similar success for life sciences too.&lt;/p&gt;
&lt;p&gt;There are several areas of life science where we see Dask being used today:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Biological image processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Single cell analysis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Statistical genetics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;…and many more&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ve solicited feedback from the life science community, to come up with a strategic plan to direct our effort over the next year.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 57)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-we-learned"&gt;
&lt;h1&gt;What we learned&lt;/h1&gt;
&lt;section id="from-dask-users"&gt;
&lt;h2&gt;From Dask users&lt;/h2&gt;
&lt;p&gt;When we talked to individual Dask users, we heard a lot of similar themes in their comments.&lt;/p&gt;
&lt;p&gt;People wanted:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Better documentation and examples&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better support for working with constrained resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better interoperability with other software tools&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most common request was for better documentation with more examples. People across many different areas of life science all said this could help them a lot. A corresponding challenge here is the multitude of different areas of life science, all of which require targeted documentation.&lt;/p&gt;
&lt;p&gt;GPU support was also commonly mentioned. Comments about GPUs fit into two of the categories above: GPU memory is often a constraint, and life scientists also want it to be easier to apply deep learning models to their data.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="from-other-software-libraries"&gt;
&lt;h2&gt;From other software libraries&lt;/h2&gt;
&lt;p&gt;We didn’t only talk with individual users of Dask. We also spoke to developers of scientific software projects.&lt;/p&gt;
&lt;section id="why-would-other-software-libraries-adopt-dask"&gt;
&lt;h3&gt;Why would other software libraries adopt Dask?&lt;/h3&gt;
&lt;p&gt;Software projects wanted to solve problems related to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Easier deployment to distributed clusters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Managing memory when processing large datasets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parallelization of existing functionality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dask is good at solving those kinds of problems, and might be a good solution for this.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="who-we-ve-talked-to"&gt;
&lt;h3&gt;Who we’ve talked to&lt;/h3&gt;
&lt;p&gt;Some of the software projects we spoke to include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://napari.org/"&gt;napari&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://pystatgen.github.io/sgkit/latest/"&gt;sgkit&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://scanpy.readthedocs.io/en/stable/"&gt;scanpy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://squidpy.readthedocs.io/en/latest/"&gt;squidpy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://cellprofiler.org/"&gt;CellProfiler&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="current-status"&gt;
&lt;h3&gt;Current status&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://napari.org/"&gt;napari&lt;/a&gt; is a python based image viewer. Dask is already well-integrated with napari. Areas for opportunity here include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Improved documentation about how to work efficiently with Dask arrays in napari.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Smarter caching of neighbouring image chunks to avoid lag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Guides for how to create plugins for napari, so the community can grow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pystatgen.github.io/sgkit/latest/"&gt;sgkit&lt;/a&gt; is a statistical genetics toolkit. Dask is already well-integrated with sgkit. The developers would like improved infrastructure in the main Dask repositories that they can benefit from. Wishlist items include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Better ways to understand how things like array chunks change as they move through a Dask computation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better high level graph visualizations. Graph visualizations showing all the low level operations can be overwhelming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better ways to identify poorly efficient areas in Dask computations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stability when new versions of Dask are released&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Making it easier to run Dask in the cloud. They are currently using &lt;a class="reference external" href="https://github.com/dask/dask-cloudprovider"&gt;dask-cloudprovider&lt;/a&gt; and finding that very useful.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="https://scanpy.readthedocs.io/en/stable/"&gt;scanpy&lt;/a&gt; is a library for single cell analysis in Python. It is built together with &lt;a class="reference external" href="https://anndata.readthedocs.io/en/latest/"&gt;anndata&lt;/a&gt;, an annotated data structure.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Data size is less of an issue for scanpy users, although anndata developers do think support for Dask would be a useful thing to add.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for sparse arrays is very important for these communities.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="https://squidpy.readthedocs.io/en/latest/"&gt;squidpy&lt;/a&gt; is a tool for the analysis and visualization of spatial molecular data. It builds on top of scanpy and anndata. Because squidpy involves large imaging data on top of what we’d normally see for datasets in scanpy/anndata, this is a project with a large area of opportunity for Dask.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Integrating Dask with the squidpy ImageContainer class is a good first step towards handling large image data within the availabe RAM constraints.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt; does not currently use Dask at all. They are curious to see if Dask can make it easier to scale up from a single machine to a cluster.
Users generally train an ilastik model interactively, and then want to apply it to many images. This second step is often when people want an easy way to scale up the computing resources available.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://cellprofiler.org/"&gt;CellProfiler&lt;/a&gt; is a pipeline tool for image processing. They have briefly experimented with Dask before.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Primarily, they want to parallelize existing functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Most common pipelines fall into three major “user stories” where focussing effort would make the most impact:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Image processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Object processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measurements&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 134)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="opportunities-we-see"&gt;
&lt;h1&gt;Opportunities we see&lt;/h1&gt;
&lt;p&gt;Because large scientific software projects have many users, improvements here would be high value for the scientific community. This is a huge area of opportunity. We plan to collaborate with these developer communities as much as possible to drive this forward.&lt;/p&gt;
&lt;p&gt;Another area of opportunity is improving infrastructure for &lt;a class="reference external" href="https://github.com/dask/dask/issues/7141"&gt;high level graph visualizations&lt;/a&gt;. Power users and novices alike would benefit from better tools for identifying areas of inefficiencies in Dask computations.&lt;/p&gt;
&lt;p&gt;Finally, continuing to build support for Dask arrays with non-numpy chunks is also a high impact area of opportunity. In particular, support for sparse arrays, and support for arrays on the GPU were highlighted as very important to the life science community.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 142)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="strategic-direction"&gt;
&lt;h1&gt;Strategic direction&lt;/h1&gt;
&lt;p&gt;We’re going to manage this project with three parallel streams:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#infrastructure"&gt;&lt;span class="xref myst"&gt;INFRASTRUCTURE&lt;/span&gt;&lt;/a&gt; (60%)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#outreach"&gt;&lt;span class="xref myst"&gt;OUTREACH&lt;/span&gt;&lt;/a&gt; (20%)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#applications"&gt;&lt;span class="xref myst"&gt;APPLICATIONS&lt;/span&gt;&lt;/a&gt; (20%)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each stream will likely have one primary project at any time, with many more queued. Within each stream, proposed projects will be ranked according to: level of impact, time commitment required, and the availability of other developer resources.&lt;/p&gt;
&lt;section id="infrastructure"&gt;
&lt;h2&gt;Infrastructure&lt;/h2&gt;
&lt;p&gt;Infrastructure projects are improvements to either:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Projects housed within the &lt;a class="reference external" href="https://github.com/dask/"&gt;Dask organisation&lt;/a&gt;, or&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Other software projects involving Dask with large numbers of life science users&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll aim to spend around 60% of project effort on infrastructure.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="outreach"&gt;
&lt;h2&gt;Outreach&lt;/h2&gt;
&lt;p&gt;Outreach activities include blogposts, talks, webinars, tutorials, and creating examples for documentation. We aim to spend around 20% of project effort on outreach.&lt;/p&gt;
&lt;p&gt;If you have outreach ideas you want to share (perhaps you run a student group or popular meetup) then you can &lt;a class="reference external" href="https://docs.google.com/forms/d/e/1FAIpQLScBi8YOx3gGkL9rz8TsRTIZYiRha9qYOvXu4EZx9qGLtjLGCw/viewform?usp=sf_link"&gt;get in touch with us here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="applications"&gt;
&lt;h2&gt;Applications&lt;/h2&gt;
&lt;p&gt;The final stream focusses on the application of Dask to a specific problem in life science.&lt;/p&gt;
&lt;p&gt;These projects generally involve collaborating with individual labs or group, and have an end goal of summarizing their workflow in a blogpost. This feeds back into our outreach, so others in the community can learn from it.&lt;/p&gt;
&lt;p&gt;Ideally these are short term projects, so we can showcase many different applications of Dask. We aim to spend around 20% of project effort on applications.&lt;/p&gt;
&lt;p&gt;If you use Dask and have an example in mind you’d like to share, then you can &lt;a class="reference external" href="https://docs.google.com/forms/d/e/1FAIpQLScBi8YOx3gGkL9rz8TsRTIZYiRha9qYOvXu4EZx9qGLtjLGCw/viewform?usp=sf_link"&gt;get in touch with us here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-will-we-know-what-success-looks-like"&gt;
&lt;h2&gt;How will we know what success looks like?&lt;/h2&gt;
&lt;p&gt;The role of Dask Life Science Fellow has a very broad scope, so there are a lot of different ways we could be successful within this space.&lt;/p&gt;
&lt;p&gt;Some indicators of success are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Bugs being clearly described, or bottlenecks clearly identified&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bug fixes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improvements or new features made to Dask infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improvements or new features made in related project repositories&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better integration or support for Dask made in related project repositories for life sciences&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better documentation with examples tailored to specific areas of life science&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blogposts written (ideally in collaboration with Dask users)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Talks given&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Webinars produced&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tutorials created&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We won’t have the time or the resources to do all the things, but we will be able to make an impact by focussing on a subset.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 196)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="limitations"&gt;
&lt;h1&gt;Limitations&lt;/h1&gt;
&lt;p&gt;The information we discovered talking to the life science community is likely to be biased in a few different ways.&lt;/p&gt;
&lt;p&gt;My (Genevieve’s) network is strongest among imaging scientists, and among people in Australia. It’s much less strong for other fields in life science, as my original training is in physics.&lt;/p&gt;
&lt;p&gt;The Dask project has strong links to other open source python projects, including scientific software. The Dask developer community also has strong links from companies including NVIDIA, Quansight, and others. They are likely to be over-represented among the people we spoke to.&lt;/p&gt;
&lt;p&gt;It’s much harder to find people who aren’t using Dask at all yet but have problems that would be a good fit for it. These people are very unlikely to be, say following &lt;a class="reference external" href="https://twitter.com/dask_dev/"&gt;Dask on twitter&lt;/a&gt;, and probably won’t be aware that we’re looking for them.&lt;/p&gt;
&lt;p&gt;I don’t think there are any perfect solutions to these problems.
We’ve tried to mitigate these effects by using loose second and third degree connections to spread awareness, as well as posting in science public forums.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 209)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="methods"&gt;
&lt;h1&gt;Methods&lt;/h1&gt;
&lt;p&gt;We used a variety of approaches to gather feedback from the life science community.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;A &lt;a class="reference external" href="https://t.co/0NeknSdrO9?amp=1"&gt;short survey&lt;/a&gt; was created to gather comments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It was advertised by the &lt;a class="reference external" href="https://twitter.com/dask_dev/"&gt;&amp;#64;dask_dev&lt;/a&gt; twitter account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We asked related software projects consider retweeting for reach (&lt;a class="reference external" href="https://twitter.com/napari_imaging/status/1360090299901505543"&gt;example&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We posted in scientific Slack groups and online public forums&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We emailed other life scientists in our network, asking them to let their networks know too&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We contacted a number of life science researchers directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We contacted several other scientific software groups directly and spoke with the developers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2021/03/04/the-life-science-community.md&lt;/span&gt;, line 221)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="join-the-discussion"&gt;
&lt;h1&gt;Join the discussion&lt;/h1&gt;
&lt;p&gt;Come join us in the Dask slack! We have a #life-science channel so there’s a place to discuss things relevant to the Dask life science community. You can &lt;a class="reference external" href="https://join.slack.com/t/dask/shared_invite/zt-mfmh7quc-nIrXL6ocgiUH2haLYA914g"&gt;request an invite to the Slack here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2021/03/04/the-life-science-community/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="imaging" label="imaging"/>
    <published>2021-03-04T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2019/08/09/image-itk/</id>
    <title>Dask and ITK for large scale image analysis</title>
    <updated>2019-08-09T00:00:00+00:00</updated>
    <author>
      <name>Matthew McCormick</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="executive-summary"&gt;

&lt;p&gt;This post explores using the &lt;a class="reference external" href="https://www.itk.org"&gt;ITK&lt;/a&gt; suite of image processing utilities in parallel with Dask Array.&lt;/p&gt;
&lt;p&gt;We cover …&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;A simple but common example of applying deconvolution across a stack of 3d images&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tips on how to make these two libraries work well together&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Challenges that we ran into and opportunities for future improvements.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 19)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="a-worked-example"&gt;
&lt;h1&gt;A Worked Example&lt;/h1&gt;
&lt;p&gt;Let’s start with a full example applying Richardson Lucy deconvolution to a
stack of light sheet microscopy data. This is the same data that we showed how
to load in our &lt;a class="reference external" href="https://blog.dask.org/2019/06/20/load-image-data"&gt;last blogpost on image loading&lt;/a&gt;.
You can &lt;a class="reference external" href="https://drive.google.com/drive/folders/13mpIfqspKTIINkfoWbFsVtFF8D7jbTqJ"&gt;access the data as tiff files from google drive here&lt;/a&gt;, and the access the &lt;a class="reference external" href="https://drive.google.com/drive/folders/13udO-h9epItG5MNWBp0VxBkKCllYBLQF"&gt;corresponding point spread function images here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Load our data from last time¶&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_zarr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AOLLSMData_m4_raw.zarr/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table&gt;  &lt;thead&gt;    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 188.74 GB &lt;/td&gt; &lt;td&gt; 316.15 MB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (3, 199, 201, 1024, 768) &lt;/td&gt; &lt;td&gt; (1, 1, 201, 1024, 768) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 598 Tasks &lt;/td&gt;&lt;td&gt; 597 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; uint16 &lt;/td&gt;&lt;td&gt; numpy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;svg width="404" height="206" style="stroke:rgb(0,0,0);stroke-width:1" &gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="0" y1="0" x2="45" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="0" y1="9" x2="45" y2="9" /&gt;
  &lt;line x1="0" y1="18" x2="45" y2="18" /&gt;
  &lt;line x1="0" y1="27" x2="45" y2="27" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="0" y1="0" x2="0" y2="27" style="stroke-width:2" /&gt;
  &lt;line x1="0" y1="0" x2="0" y2="27" /&gt;
  &lt;line x1="0" y1="0" x2="0" y2="27" /&gt;
  &lt;line x1="0" y1="0" x2="0" y2="27" /&gt;
  &lt;line x1="0" y1="0" x2="0" y2="27" /&gt;
  &lt;line x1="1" y1="0" x2="1" y2="27" /&gt;
  &lt;line x1="1" y1="0" x2="1" y2="27" /&gt;
  &lt;line x1="1" y1="0" x2="1" y2="27" /&gt;
  &lt;line x1="1" y1="0" x2="1" y2="27" /&gt;
  &lt;line x1="2" y1="0" x2="2" y2="27" /&gt;
  &lt;line x1="2" y1="0" x2="2" y2="27" /&gt;
  &lt;line x1="2" y1="0" x2="2" y2="27" /&gt;
  &lt;line x1="2" y1="0" x2="2" y2="27" /&gt;
  &lt;line x1="2" y1="0" x2="2" y2="27" /&gt;
  &lt;line x1="3" y1="0" x2="3" y2="27" /&gt;
  &lt;line x1="3" y1="0" x2="3" y2="27" /&gt;
  &lt;line x1="3" y1="0" x2="3" y2="27" /&gt;
  &lt;line x1="3" y1="0" x2="3" y2="27" /&gt;
  &lt;line x1="4" y1="0" x2="4" y2="27" /&gt;
  &lt;line x1="4" y1="0" x2="4" y2="27" /&gt;
  &lt;line x1="4" y1="0" x2="4" y2="27" /&gt;
  &lt;line x1="4" y1="0" x2="4" y2="27" /&gt;
  &lt;line x1="5" y1="0" x2="5" y2="27" /&gt;
  &lt;line x1="5" y1="0" x2="5" y2="27" /&gt;
  &lt;line x1="5" y1="0" x2="5" y2="27" /&gt;
  &lt;line x1="5" y1="0" x2="5" y2="27" /&gt;
  &lt;line x1="5" y1="0" x2="5" y2="27" /&gt;
  &lt;line x1="6" y1="0" x2="6" y2="27" /&gt;
  &lt;line x1="6" y1="0" x2="6" y2="27" /&gt;
  &lt;line x1="6" y1="0" x2="6" y2="27" /&gt;
  &lt;line x1="6" y1="0" x2="6" y2="27" /&gt;
  &lt;line x1="7" y1="0" x2="7" y2="27" /&gt;
  &lt;line x1="7" y1="0" x2="7" y2="27" /&gt;
  &lt;line x1="7" y1="0" x2="7" y2="27" /&gt;
  &lt;line x1="7" y1="0" x2="7" y2="27" /&gt;
  &lt;line x1="7" y1="0" x2="7" y2="27" /&gt;
  &lt;line x1="8" y1="0" x2="8" y2="27" /&gt;
  &lt;line x1="8" y1="0" x2="8" y2="27" /&gt;
  &lt;line x1="8" y1="0" x2="8" y2="27" /&gt;
  &lt;line x1="8" y1="0" x2="8" y2="27" /&gt;
  &lt;line x1="9" y1="0" x2="9" y2="27" /&gt;
  &lt;line x1="9" y1="0" x2="9" y2="27" /&gt;
  &lt;line x1="9" y1="0" x2="9" y2="27" /&gt;
  &lt;line x1="9" y1="0" x2="9" y2="27" /&gt;
  &lt;line x1="10" y1="0" x2="10" y2="27" /&gt;
  &lt;line x1="10" y1="0" x2="10" y2="27" /&gt;
  &lt;line x1="10" y1="0" x2="10" y2="27" /&gt;
  &lt;line x1="10" y1="0" x2="10" y2="27" /&gt;
  &lt;line x1="10" y1="0" x2="10" y2="27" /&gt;
  &lt;line x1="11" y1="0" x2="11" y2="27" /&gt;
  &lt;line x1="11" y1="0" x2="11" y2="27" /&gt;
  &lt;line x1="11" y1="0" x2="11" y2="27" /&gt;
  &lt;line x1="11" y1="0" x2="11" y2="27" /&gt;
  &lt;line x1="12" y1="0" x2="12" y2="27" /&gt;
  &lt;line x1="12" y1="0" x2="12" y2="27" /&gt;
  &lt;line x1="12" y1="0" x2="12" y2="27" /&gt;
  &lt;line x1="12" y1="0" x2="12" y2="27" /&gt;
  &lt;line x1="12" y1="0" x2="12" y2="27" /&gt;
  &lt;line x1="13" y1="0" x2="13" y2="27" /&gt;
  &lt;line x1="13" y1="0" x2="13" y2="27" /&gt;
  &lt;line x1="13" y1="0" x2="13" y2="27" /&gt;
  &lt;line x1="13" y1="0" x2="13" y2="27" /&gt;
  &lt;line x1="14" y1="0" x2="14" y2="27" /&gt;
  &lt;line x1="14" y1="0" x2="14" y2="27" /&gt;
  &lt;line x1="14" y1="0" x2="14" y2="27" /&gt;
  &lt;line x1="14" y1="0" x2="14" y2="27" /&gt;
  &lt;line x1="15" y1="0" x2="15" y2="27" /&gt;
  &lt;line x1="15" y1="0" x2="15" y2="27" /&gt;
  &lt;line x1="15" y1="0" x2="15" y2="27" /&gt;
  &lt;line x1="15" y1="0" x2="15" y2="27" /&gt;
  &lt;line x1="15" y1="0" x2="15" y2="27" /&gt;
  &lt;line x1="16" y1="0" x2="16" y2="27" /&gt;
  &lt;line x1="16" y1="0" x2="16" y2="27" /&gt;
  &lt;line x1="16" y1="0" x2="16" y2="27" /&gt;
  &lt;line x1="16" y1="0" x2="16" y2="27" /&gt;
  &lt;line x1="17" y1="0" x2="17" y2="27" /&gt;
  &lt;line x1="17" y1="0" x2="17" y2="27" /&gt;
  &lt;line x1="17" y1="0" x2="17" y2="27" /&gt;
  &lt;line x1="17" y1="0" x2="17" y2="27" /&gt;
  &lt;line x1="18" y1="0" x2="18" y2="27" /&gt;
  &lt;line x1="18" y1="0" x2="18" y2="27" /&gt;
  &lt;line x1="18" y1="0" x2="18" y2="27" /&gt;
  &lt;line x1="18" y1="0" x2="18" y2="27" /&gt;
  &lt;line x1="18" y1="0" x2="18" y2="27" /&gt;
  &lt;line x1="19" y1="0" x2="19" y2="27" /&gt;
  &lt;line x1="19" y1="0" x2="19" y2="27" /&gt;
  &lt;line x1="19" y1="0" x2="19" y2="27" /&gt;
  &lt;line x1="19" y1="0" x2="19" y2="27" /&gt;
  &lt;line x1="20" y1="0" x2="20" y2="27" /&gt;
  &lt;line x1="20" y1="0" x2="20" y2="27" /&gt;
  &lt;line x1="20" y1="0" x2="20" y2="27" /&gt;
  &lt;line x1="20" y1="0" x2="20" y2="27" /&gt;
  &lt;line x1="20" y1="0" x2="20" y2="27" /&gt;
  &lt;line x1="21" y1="0" x2="21" y2="27" /&gt;
  &lt;line x1="21" y1="0" x2="21" y2="27" /&gt;
  &lt;line x1="21" y1="0" x2="21" y2="27" /&gt;
  &lt;line x1="21" y1="0" x2="21" y2="27" /&gt;
  &lt;line x1="22" y1="0" x2="22" y2="27" /&gt;
  &lt;line x1="22" y1="0" x2="22" y2="27" /&gt;
  &lt;line x1="22" y1="0" x2="22" y2="27" /&gt;
  &lt;line x1="22" y1="0" x2="22" y2="27" /&gt;
  &lt;line x1="23" y1="0" x2="23" y2="27" /&gt;
  &lt;line x1="23" y1="0" x2="23" y2="27" /&gt;
  &lt;line x1="23" y1="0" x2="23" y2="27" /&gt;
  &lt;line x1="23" y1="0" x2="23" y2="27" /&gt;
  &lt;line x1="23" y1="0" x2="23" y2="27" /&gt;
  &lt;line x1="24" y1="0" x2="24" y2="27" /&gt;
  &lt;line x1="24" y1="0" x2="24" y2="27" /&gt;
  &lt;line x1="24" y1="0" x2="24" y2="27" /&gt;
  &lt;line x1="24" y1="0" x2="24" y2="27" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="27" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="27" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="27" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="27" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="27" /&gt;
  &lt;line x1="26" y1="0" x2="26" y2="27" /&gt;
  &lt;line x1="26" y1="0" x2="26" y2="27" /&gt;
  &lt;line x1="26" y1="0" x2="26" y2="27" /&gt;
  &lt;line x1="26" y1="0" x2="26" y2="27" /&gt;
  &lt;line x1="27" y1="0" x2="27" y2="27" /&gt;
  &lt;line x1="27" y1="0" x2="27" y2="27" /&gt;
  &lt;line x1="27" y1="0" x2="27" y2="27" /&gt;
  &lt;line x1="27" y1="0" x2="27" y2="27" /&gt;
  &lt;line x1="28" y1="0" x2="28" y2="27" /&gt;
  &lt;line x1="28" y1="0" x2="28" y2="27" /&gt;
  &lt;line x1="28" y1="0" x2="28" y2="27" /&gt;
  &lt;line x1="28" y1="0" x2="28" y2="27" /&gt;
  &lt;line x1="28" y1="0" x2="28" y2="27" /&gt;
  &lt;line x1="29" y1="0" x2="29" y2="27" /&gt;
  &lt;line x1="29" y1="0" x2="29" y2="27" /&gt;
  &lt;line x1="29" y1="0" x2="29" y2="27" /&gt;
  &lt;line x1="29" y1="0" x2="29" y2="27" /&gt;
  &lt;line x1="30" y1="0" x2="30" y2="27" /&gt;
  &lt;line x1="30" y1="0" x2="30" y2="27" /&gt;
  &lt;line x1="30" y1="0" x2="30" y2="27" /&gt;
  &lt;line x1="30" y1="0" x2="30" y2="27" /&gt;
  &lt;line x1="31" y1="0" x2="31" y2="27" /&gt;
  &lt;line x1="31" y1="0" x2="31" y2="27" /&gt;
  &lt;line x1="31" y1="0" x2="31" y2="27" /&gt;
  &lt;line x1="31" y1="0" x2="31" y2="27" /&gt;
  &lt;line x1="31" y1="0" x2="31" y2="27" /&gt;
  &lt;line x1="32" y1="0" x2="32" y2="27" /&gt;
  &lt;line x1="32" y1="0" x2="32" y2="27" /&gt;
  &lt;line x1="32" y1="0" x2="32" y2="27" /&gt;
  &lt;line x1="32" y1="0" x2="32" y2="27" /&gt;
  &lt;line x1="33" y1="0" x2="33" y2="27" /&gt;
  &lt;line x1="33" y1="0" x2="33" y2="27" /&gt;
  &lt;line x1="33" y1="0" x2="33" y2="27" /&gt;
  &lt;line x1="33" y1="0" x2="33" y2="27" /&gt;
  &lt;line x1="33" y1="0" x2="33" y2="27" /&gt;
  &lt;line x1="34" y1="0" x2="34" y2="27" /&gt;
  &lt;line x1="34" y1="0" x2="34" y2="27" /&gt;
  &lt;line x1="34" y1="0" x2="34" y2="27" /&gt;
  &lt;line x1="34" y1="0" x2="34" y2="27" /&gt;
  &lt;line x1="35" y1="0" x2="35" y2="27" /&gt;
  &lt;line x1="35" y1="0" x2="35" y2="27" /&gt;
  &lt;line x1="35" y1="0" x2="35" y2="27" /&gt;
  &lt;line x1="35" y1="0" x2="35" y2="27" /&gt;
  &lt;line x1="36" y1="0" x2="36" y2="27" /&gt;
  &lt;line x1="36" y1="0" x2="36" y2="27" /&gt;
  &lt;line x1="36" y1="0" x2="36" y2="27" /&gt;
  &lt;line x1="36" y1="0" x2="36" y2="27" /&gt;
  &lt;line x1="36" y1="0" x2="36" y2="27" /&gt;
  &lt;line x1="37" y1="0" x2="37" y2="27" /&gt;
  &lt;line x1="37" y1="0" x2="37" y2="27" /&gt;
  &lt;line x1="37" y1="0" x2="37" y2="27" /&gt;
  &lt;line x1="37" y1="0" x2="37" y2="27" /&gt;
  &lt;line x1="38" y1="0" x2="38" y2="27" /&gt;
  &lt;line x1="38" y1="0" x2="38" y2="27" /&gt;
  &lt;line x1="38" y1="0" x2="38" y2="27" /&gt;
  &lt;line x1="38" y1="0" x2="38" y2="27" /&gt;
  &lt;line x1="38" y1="0" x2="38" y2="27" /&gt;
  &lt;line x1="39" y1="0" x2="39" y2="27" /&gt;
  &lt;line x1="39" y1="0" x2="39" y2="27" /&gt;
  &lt;line x1="39" y1="0" x2="39" y2="27" /&gt;
  &lt;line x1="39" y1="0" x2="39" y2="27" /&gt;
  &lt;line x1="40" y1="0" x2="40" y2="27" /&gt;
  &lt;line x1="40" y1="0" x2="40" y2="27" /&gt;
  &lt;line x1="40" y1="0" x2="40" y2="27" /&gt;
  &lt;line x1="40" y1="0" x2="40" y2="27" /&gt;
  &lt;line x1="41" y1="0" x2="41" y2="27" /&gt;
  &lt;line x1="41" y1="0" x2="41" y2="27" /&gt;
  &lt;line x1="41" y1="0" x2="41" y2="27" /&gt;
  &lt;line x1="41" y1="0" x2="41" y2="27" /&gt;
  &lt;line x1="41" y1="0" x2="41" y2="27" /&gt;
  &lt;line x1="42" y1="0" x2="42" y2="27" /&gt;
  &lt;line x1="42" y1="0" x2="42" y2="27" /&gt;
  &lt;line x1="42" y1="0" x2="42" y2="27" /&gt;
  &lt;line x1="42" y1="0" x2="42" y2="27" /&gt;
  &lt;line x1="43" y1="0" x2="43" y2="27" /&gt;
  &lt;line x1="43" y1="0" x2="43" y2="27" /&gt;
  &lt;line x1="43" y1="0" x2="43" y2="27" /&gt;
  &lt;line x1="43" y1="0" x2="43" y2="27" /&gt;
  &lt;line x1="44" y1="0" x2="44" y2="27" /&gt;
  &lt;line x1="44" y1="0" x2="44" y2="27" /&gt;
  &lt;line x1="44" y1="0" x2="44" y2="27" /&gt;
  &lt;line x1="44" y1="0" x2="44" y2="27" /&gt;
  &lt;line x1="44" y1="0" x2="44" y2="27" /&gt;
  &lt;line x1="45" y1="0" x2="45" y2="27" /&gt;
  &lt;line x1="45" y1="0" x2="45" y2="27" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="0.000000,0.000000 45.378219,0.000000 45.378219,27.530335 0.000000,27.530335" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="22.689110" y="47.530335" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;199&lt;/text&gt;
&lt;text x="65.378219" y="13.765167" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(0,65.378219,13.765167)"&gt;3&lt;/text&gt;&lt;/p&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="115" y1="0" x2="141" y2="26" style="stroke-width:2" /&gt;
  &lt;line x1="115" y1="130" x2="141" y2="156" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="115" y1="0" x2="115" y2="130" style="stroke-width:2" /&gt;
  &lt;line x1="141" y1="26" x2="141" y2="156" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="115.000000,0.000000 141.720328,26.720328 141.720328,156.720328 115.000000,130.000000" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="115" y1="0" x2="212" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="141" y1="26" x2="239" y2="26" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="115" y1="0" x2="141" y2="26" style="stroke-width:2" /&gt;
  &lt;line x1="212" y1="0" x2="239" y2="26" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="115.000000,0.000000 212.500000,0.000000 239.220328,26.720328 141.720328,26.720328" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="141" y1="26" x2="239" y2="26" style="stroke-width:2" /&gt;
  &lt;line x1="141" y1="156" x2="239" y2="156" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="141" y1="26" x2="141" y2="156" style="stroke-width:2" /&gt;
  &lt;line x1="239" y1="26" x2="239" y2="156" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="141.720328,26.720328 239.220328,26.720328 239.220328,156.720328 141.720328,156.720328" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="190.470328" y="176.720328" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;768&lt;/text&gt;
&lt;text x="259.220328" y="91.720328" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(-90,259.220328,91.720328)"&gt;1024&lt;/text&gt;
&lt;text x="118.360164" y="163.360164" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(45,118.360164,163.360164)"&gt;201&lt;/text&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;This dataset has shape &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;(3,&lt;/span&gt; &lt;span class="pre"&gt;199,&lt;/span&gt; &lt;span class="pre"&gt;201,&lt;/span&gt; &lt;span class="pre"&gt;1024,&lt;/span&gt; &lt;span class="pre"&gt;768)&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;3 fluorescence color channels,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;199 time points,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;201 z-slices,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1024 pixels in the y dimension, and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;768 pixels in the x dimension.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Load our Point Spread Function (PSF)&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array.image&lt;/span&gt;
&lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AOLLSMData/m4/psfs_z0p1/*.tif&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[:,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table&gt;  &lt;thead&gt;    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 2.48 MB &lt;/td&gt; &lt;td&gt; 827.39 kB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (3, 1, 101, 64, 64) &lt;/td&gt; &lt;td&gt; (1, 1, 101, 64, 64) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 6 Tasks &lt;/td&gt;&lt;td&gt; 3 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; uint16 &lt;/td&gt;&lt;td&gt; numpy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;svg width="402" height="208" style="stroke:rgb(0,0,0);stroke-width:1" &gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="0" y1="0" x2="27" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="0" y1="11" x2="27" y2="11" /&gt;
  &lt;line x1="0" y1="22" x2="27" y2="22" /&gt;
  &lt;line x1="0" y1="33" x2="27" y2="33" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="0" y1="0" x2="0" y2="33" style="stroke-width:2" /&gt;
  &lt;line x1="27" y1="0" x2="27" y2="33" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="0.000000,0.000000 27.530335,0.000000 27.530335,33.941765 0.000000,33.941765" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="13.765167" y="53.941765" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;1&lt;/text&gt;
&lt;text x="47.530335" y="16.970882" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(0,47.530335,16.970882)"&gt;3&lt;/text&gt;&lt;/p&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="97" y1="0" x2="173" y2="76" style="stroke-width:2" /&gt;
  &lt;line x1="97" y1="82" x2="173" y2="158" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="97" y1="0" x2="97" y2="82" style="stroke-width:2" /&gt;
  &lt;line x1="173" y1="76" x2="173" y2="158" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="97.000000,0.000000 173.470588,76.470588 173.470588,158.846826 97.000000,82.376238" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="97" y1="0" x2="179" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="173" y1="76" x2="255" y2="76" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="97" y1="0" x2="173" y2="76" style="stroke-width:2" /&gt;
  &lt;line x1="179" y1="0" x2="255" y2="76" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="97.000000,0.000000 179.376238,0.000000 255.846826,76.470588 173.470588,76.470588" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="173" y1="76" x2="255" y2="76" style="stroke-width:2" /&gt;
  &lt;line x1="173" y1="158" x2="255" y2="158" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="173" y1="76" x2="173" y2="158" style="stroke-width:2" /&gt;
  &lt;line x1="255" y1="76" x2="255" y2="158" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="173.470588,76.470588 255.846826,76.470588 255.846826,158.846826 173.470588,158.846826" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="214.658707" y="178.846826" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;64&lt;/text&gt;
&lt;text x="275.846826" y="117.658707" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(0,275.846826,117.658707)"&gt;64&lt;/text&gt;
&lt;text x="125.235294" y="140.611532" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(45,125.235294,140.611532)"&gt;101&lt;/text&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Convert data to float32 for computation¶&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Note: the psf needs to be sampled with a voxel spacing&lt;/span&gt;
&lt;span class="c1"&gt;# consistent with the image&amp;#39;s sampling&lt;/span&gt;
&lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Apply Richardson-Lucy Deconvolution¶&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Apply deconvolution to a single chunk of data &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itk&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;
    &lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;

    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Convert to ITK object&lt;/span&gt;
    &lt;span class="n"&gt;kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Convert to ITK object&lt;/span&gt;

    &lt;span class="n"&gt;deconvolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution_image_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;kernel_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;number_of_iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array_from_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deconvolved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Convert back to Numpy array&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Add back the leading length-one dimensions&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create a local cluster of dask worker processes&lt;/span&gt;
&lt;span class="c1"&gt;# (this could also point to a distributed cluster if you have it)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threads_per_process&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# now dask operations use this cluster by default&lt;/span&gt;

&lt;span class="c1"&gt;# Trigger computation and store&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_zarr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;AOLLSMData_m4_raw.zarr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;deconvolved&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overwrite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So in the example above we …&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Load data both from Zarr and TIFF files into multi-chunked Dask arrays&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Construct a function to apply an ITK routine onto each chunk&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply that function across the dask array with the &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html#dask.array.core.map_blocks"&gt;dask.array.map_blocks&lt;/a&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store the result back into Zarr format&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From the perspective of an imaging scientist,
the new piece of technology here is the
&lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html#dask.array.core.map_blocks"&gt;dask.array.map_blocks&lt;/a&gt; function.
Given a Dask array composed of many NumPy arrays and a function, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; applies that function across each block in parallel, returning a Dask array as a result.
It’s a great tool whenever you want to apply an operation across many blocks in a simple fashion.
Because Dask arrays are just made out of Numpy arrays it’s an easy way to
compose Dask with the rest of the Scientific Python ecosystem.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 459)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="building-the-right-function"&gt;
&lt;h1&gt;Building the right function&lt;/h1&gt;
&lt;p&gt;However in this case there are a few challenges to constructing the right Numpy
-&amp;gt; Numpy function, due to both idiosyncrasies in ITK and Dask Array. Let’s
look at our function again:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Apply deconvolution to a single chunk of data &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itk&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;
    &lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;

    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Convert to ITK object&lt;/span&gt;
    &lt;span class="n"&gt;kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Convert to ITK object&lt;/span&gt;

    &lt;span class="n"&gt;deconvolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution_image_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;kernel_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;number_of_iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array_from_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deconvolved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Convert back to Numpy array&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Add back the leading length-one dimensions&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is longer than we would like.
Instead, we would have preferred to just use the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;itk&lt;/span&gt;&lt;/code&gt; function directly,
without all of the steps before and after.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;deconvolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution_image_filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;What were the extra steps in our function and why were they necessary?&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Convert to and from ITK Image objects&lt;/strong&gt;: ITK functions don’t consume and
produce Numpy arrays, they consume and produce their own &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Image&lt;/span&gt;&lt;/code&gt; data
structure. There are convenient functions to convert back and forth,
so handling this is straightforward, but it does need to be handled each
time. See &lt;a class="reference external" href="https://github.com/InsightSoftwareConsortium/ITK/issues/1136"&gt;ITK #1136&lt;/a&gt; for a
feature request that would remove the need for this step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unpack and pack singleton dimensions&lt;/strong&gt;: Our Dask arrays have shapes like
the following:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Chunk&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So our &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; function gets NumPy arrays of the chunk size,
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;(1,&lt;/span&gt; &lt;span class="pre"&gt;1,&lt;/span&gt; &lt;span class="pre"&gt;201,&lt;/span&gt; &lt;span class="pre"&gt;1024,&lt;/span&gt; &lt;span class="pre"&gt;768)&lt;/span&gt;&lt;/code&gt;.
However, our ITK functions are meant to work on 3d arrays, not 5d arrays,
so we need to remove those first two dimensions.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;
&lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# remove leading two length-one dimensions&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And then when we’re done, Dask expects to get back 5d arrays like what it
provided, so we add these singleton dimensions back in&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Add back the leading length-one dimensions&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Again, this is straightforward for users who are accustomed to NumPy
slicing syntax, but does need to be done each time.
This adds some friction to our development process,
and is another step that can confuse users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But if you’re comfortable working around things like this,
then ITK and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; can be a powerful combination
if you want to parallelize out ITK operations across a cluster.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 541)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="defining-a-dask-cluster"&gt;
&lt;h1&gt;Defining a Dask Cluster&lt;/h1&gt;
&lt;p&gt;Above we used &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.distributed.LocalCluster&lt;/span&gt;&lt;/code&gt; to set up 20 single-threaded
workers on our local workstation:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threads_per_process&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# now dask operations use this cluster by default&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you had a distributed resource, this is where you would connect it.
You would swap out &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LocalCluster&lt;/span&gt;&lt;/code&gt; with one of
&lt;a class="reference external" href="https://docs.dask.org/en/latest/setup.html"&gt;Dask’s other deployment options&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, we found that we needed to use many single-threaded processes rather than
one multi-threaded process because ITK functions seem to still hold onto the
GIL. This is fine, we just need to be aware of it so that we set up our Dask
workers appropriately with one thread per process for maximum efficiency.
See &lt;a class="reference external" href="https://github.com/InsightSoftwareConsortium/ITK/issues/1134"&gt;ITK #1134&lt;/a&gt;
for an active Github issue on this topic.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 563)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="serialization"&gt;
&lt;h1&gt;Serialization&lt;/h1&gt;
&lt;p&gt;We had some difficulty when using the ITK library across multiple processes,
because the library itself didn’t serialize well. (If you don’t understand
what that means, don’t worry). We solved a bit of this in
&lt;a class="reference external" href="https://github.com/InsightSoftwareConsortium/ITK/pull/1090"&gt;ITK #1090&lt;/a&gt;,
but some issues still remain.&lt;/p&gt;
&lt;p&gt;We got around this by including the import in the function rather than outside
of it.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;itk&lt;/span&gt;   &lt;span class="c1"&gt;# &amp;lt;--- we work around serialization issues by importing within the function&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That way each task imports itk individually, and we sidestep this issue.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 581)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="trying-scikit-image"&gt;
&lt;h1&gt;Trying Scikit-Image&lt;/h1&gt;
&lt;p&gt;We also tried out the Richardson Lucy deconvolution operation in
&lt;a class="reference external" href="https://scikit-image.org/"&gt;Scikit-Image&lt;/a&gt;. Scikit-Image is known for being
more Scipy/Numpy native, but not always as fast as ITK. Our experience
confirmed this perception.&lt;/p&gt;
&lt;p&gt;First, we were glad to see that the scikit-image function worked with
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; immediately without any packing/unpacking, dimensionality, or
serialization issues:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;skimage.restoration&lt;/span&gt;

&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skimage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;restoration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# just works&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So all of that converting to and from image objects or removing and adding
singleton dimensions isn’t necessary here.&lt;/p&gt;
&lt;p&gt;In terms of performance we were also happy to see that Scikit-Image released
the GIL, so we were able to get very high reported CPU utilization when using a
small number of multi-threaded processes. However, even though CPU utilization
was high, our parallel performance was poor enough that we stuck with the ITK
solution, warts and all. More information about this is available in
Github issue &lt;a class="reference external" href="https://github.com/scikit-image/scikit-image/issues/4083"&gt;scikit-image #4083&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: sequentially on a single chunk, ITK ran in around 2 minutes while
scikit-image ran in 3 minutes. It was only once we started parallelizing that
things became slow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Regardless, our goal in this experiment was to see how well ITK and Dask
array played together. It was nice to see what smooth integration looks like,
if only to motivate future development in ITK+Dask relations.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 616)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="numba-gufuncs"&gt;
&lt;h1&gt;Numba GUFuncs&lt;/h1&gt;
&lt;p&gt;An alternative to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;da.map_blocks&lt;/span&gt;&lt;/code&gt; are Generalized Universal Functions (gufuncs)
These are functions that have many magical properties, one of which is that
they operate equally well on both NumPy and Dask arrays. If libraries like
ITK or Scikit-Image make their functions into gufuncs then they work without
users having to do anything special.&lt;/p&gt;
&lt;p&gt;The easiest way to implement gufuncs today is with Numba. I did this on our
wrapped &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;richardson_lucy&lt;/span&gt;&lt;/code&gt; function, just to show how it could work, in case
other libraries want to take this on in the future.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numba&lt;/span&gt;

&lt;span class="nd"&gt;@numba&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;guvectorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;float32[:,:,:], float32[:,:,:], float32[:,:,:]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# we have to specify types&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;(i,j,k),(a,b,c)-&amp;gt;(i,j,k)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="c1"&gt;# and dimensionality explicitly&lt;/span&gt;
    &lt;span class="n"&gt;forceobj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# &amp;lt;---- no dimension unpacking!&lt;/span&gt;
    &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ascontiguousarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_view_from_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ascontiguousarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;deconvolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;richardson_lucy_deconvolution_image_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kernel_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number_of_iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;itk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array_from_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deconvolved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Now this function works natively on either NumPy or Dask arrays&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;richardson_lucy_deconvolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- no map_blocks call!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Note that we’ve both lost the dimension unpacking and the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; call.
Our function now knows enough information about how it can broadcast that Dask
can do the parallelization without being told what to do explicitly.&lt;/p&gt;
&lt;p&gt;This adds some burden onto library maintainers,
but makes the user experience much more smooth.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 658)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="gpu-acceleration"&gt;
&lt;h1&gt;GPU Acceleration&lt;/h1&gt;
&lt;p&gt;When doing some user research on image processing and Dask, almost everyone we
interviewed said that they wanted faster deconvolution. This seemed to be a
major pain point. Now we know why. It’s both very common, and &lt;em&gt;very slow&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Running deconvolution on a single chunk of this size takes around 2-4 minutes,
and we have hundreds of chunks in a single dataset. Multi-core parallelism can
help a bit here, but this problem may also be ripe for GPU acceleration.
Similar operations typically have 100x speedups on GPUs. This might be a more
pragmatic solution than scaling out to large distributed clusters.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2019/08/09/image-itk.md&lt;/span&gt;, line 670)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-s-next"&gt;
&lt;h1&gt;What’s next?&lt;/h1&gt;
&lt;p&gt;This experiment both …&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gives us an example&lt;/strong&gt; that other imaging scientists
can copy and modify to be effective with Dask and ITK together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Highlights areas of improvement&lt;/strong&gt; where developers from the different
libraries can work to remove some of these rough interactions spots in the
future.&lt;/p&gt;
&lt;p&gt;It’s worth noting that Dask has done this with lots of libraries within the
Scipy ecosystem, including Pandas, Scikit-Image, Scikit-Learn, and others.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’re also going to continue with our imaging experiment, while these technical
issues get worked out in the background. Next up, segmentation!&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2019/08/09/image-itk/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="imaging" label="imaging"/>
    <published>2019-08-09T00:00:00+00:00</published>
  </entry>
</feed>
