<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>I Love Symposia!</title><link>https://ilovesymposia.com/</link><description>Science &amp; Tech for the Small Fry</description><atom:link href="https://ilovesymposia.com/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2019 &lt;a href="mailto:jni.soma@fastmail.com"&gt;Juan Nunez-Iglesias&lt;/a&gt; 
&lt;a rel="license" href="https://creativecommons.org/licenses/by/4.0/"&gt;
&lt;img alt="Creative Commons License BY"
style="border-width:0; margin-bottom:12px;"
src="https://i.creativecommons.org/l/by/4.0/88x31.png"&gt;&lt;/a&gt;</copyright><lastBuildDate>Thu, 14 Nov 2019 13:47:48 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Introducing napari: a fast n-dimensional image viewer in Python</title><link>https://ilovesymposia.com/2019/10/24/introducing-napari-a-fast-n-dimensional-image-viewer-in-python/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;I'm really excited to finally, officially, share a new(ish) project called
napari with the world.
We have been developing napari &lt;a href="https://github.com/napari/napari"&gt;in the open&lt;/a&gt;
from the very first commit, but we didn't want to make any premature fanfare
about it… Until now. It's still alpha software, but for months now, both the
core napari team and a few collaborators/early adopters have been using napari
in our daily work. I've found it life-changing.&lt;/p&gt;
&lt;h2&gt;The background&lt;/h2&gt;
&lt;p&gt;I've been looking for a great nD volume viewer in Python for the better part of
a decade. In 2009, I joined Mitya Chklovskii's lab and the FlyEM team at the
Janelia [Farm] Research Campus to work on the segmentation of 3D electron
microscopy (EM) volumes. I started out in Matlab, but moved to Python pretty
quickly and it was a very smooth transition (highly recommended! ;). Looking at
my data was always annoying though. I was either looking at single 2D slices
using &lt;code&gt;matplotlib.pyplot.imshow&lt;/code&gt;, or saving the volumes in VTK format and
loading them into ITK-SNAP — which worked ok but required me to constantly be
changing terminals, and anyway was pretty inefficient because my disk ended up
cluttered with “temporary” VTK volumes.&lt;/p&gt;
&lt;p&gt;I tried a bunch of things after that time, including Mayavi and even building my
own orthogonal views viewer with Matplotlib (which was super slow), but nothing
really clicked. What's worse, I didn't see any movement on this — I even found
a very discouraging thread on MNE-Python in which Gaël Varoquaux concluded,
given the lack of support, that 3D visualisation was not mission critical to
anyone and not worth pursuing further. (!) Throughout that time, I kept living
with either manually picking out 2D slices, or going back to tools outside of
Python, with all the context switching that entailed. The end result is
that I was looking at my data far less than I should have been, which slowed
down my work. I can't count the number of silly mistakes I made and dead ends I
chased, just because I wasn't looking at the data enough.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;h2&gt;The beginning&lt;/h2&gt;
&lt;p&gt;In the meantime though, I was busy improving support for n-dimensional images
in scikit-image. In May last year, Nelle Varoquaux organised a joint sprint with
developers from scikit-learn, scikit-image, and dask at UC Berkeley, and I was
one of the lucky invitees. I'd recently seen that
&lt;a href="https://twitter.com/loicaroyer"&gt;Loïc Royer&lt;/a&gt;, a good friend
from my Janelia days, had moved to San Francisco to start his lab at the Chan
Zuckerberg Biohub, so I asked him if I could crash at his place for a few days
before the sprint.&lt;/p&gt;
&lt;p&gt;I knew Loïc as a Java guy. In Janelia he'd repeatedly extolled the virtues of
Java to me, and since then he'd worked on some incredible Java software,
including ClearVolume, a super-fast 3D viewer, and ClearControl, a super-fast
microscope control library (you might detect a theme there ;).  In fact,
although I love Python and its ecosystem and what we've built with
scikit-image, I've always been intimidated by Fiji and its ecosystem and its
&lt;del&gt;10,000&lt;/del&gt; 15,000 citations. Was I deluding myself that Python was important in
biology?&lt;/p&gt;
&lt;p&gt;So I was both shocked and delighted when Loïc casually mentioned that he needed
a fast nD viewer in Python, and that we should build it together. Loïc had been
pushing the state of the art of deep learning in microscopy (and continues to
do so), and, despite the hard work of many to integrate Fiji with the major
deep learning learning libraries (which are Python), as well as the wider
scientific Python ecosystem, it remains challenging to use both together. It
might well remain so, because the build, installation, and dependency tooling
is so disparate between the two communities.  And so that evening, in Loïc's
apartment, napari (lowercase n) was born (though it did not yet have a name).&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://ilovesymposia.com/napari/loic-walking-sf.jpg" alt="Loïc walking SF" style="width:100%"&gt;
  &lt;figcaption&gt;&lt;em&gt;Loïc walking around San Francisco the weekend after I landed.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Following on from his work on ClearVolume, Loïc and I agreed on some founding
design principles for our tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;n-dimensional first;&lt;/li&gt;
&lt;li&gt;native (neither of us likes opening a browser to work);&lt;/li&gt;
&lt;li&gt;blazing fast (GPU-powered);&lt;/li&gt;
&lt;li&gt;native 2D and 3D viewing; and&lt;/li&gt;
&lt;li&gt;minimal new windows and pop-ups, choosing layering instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In terms of speed, we knew it was doable, thanks to Martin Weigert's excellent
3D volume viewer, &lt;a href="https://github.com/maweigert/spimagine"&gt;spimagine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The next week, at the Berkeley sprint, I finally met Kira Evans, a contributor
to scikit-image who had made several incredibly technical pull requests, on my
favourite topic of extending n-dimensional support, and whom I'd thus asked
Nelle to invite to the sprint. She had turned out to be a freshman at
Northeastern University, but despite her age she was already doing great things
in open source. In general, life is a big accumulation of small decisions you
don't even think about. On certain rare occasions, though, you make decisions
on which you look back and think, hot damn that was a good decision! Inviting
Kira is one of those for me. (Nelle agreed.)&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://ilovesymposia.com/napari/kira-john-emma-me-dask.jpg" alt="John explains dask-image" style="width:100%"&gt;
  &lt;figcaption&gt;&lt;em&gt;John Kirkham explains dask-image to Kira, Emmanuelle Gouillart, and me at the Berkeley sprint.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;That week at the sprint, Loïc was able to hack out a quick prototype.
Meanwhile, Kira was busy helping scikit-image with n-dimensional rotations,
LowLevelCallables in C and Cython, and various other crazy things.&lt;/p&gt;
&lt;p&gt;I'll take a short moment to acknowledge &lt;a href="http://vispy.org"&gt;VisPy&lt;/a&gt;, which is the
basis for all of napari's visualisation: napari's canvas is a VisPy canvas, its
events are VisPy events, and its visual building blocks are VisPy Visuals. I
don't know the history of VisPy, but I want to thank all its
&lt;a href="https://github.com/vispy/vispy/graphs/contributors"&gt;contributors&lt;/a&gt;, especially
Almar Klein, who drove it for a very long time, and David Hoese, who
resurrected it when Almar could no longer maintain it, and who maintains
it today. VisPy is yet another example of open source software being
&lt;a href="https://ilovesymposia.com/2019/05/28/why-citations-are-not-enough-for-open-source-software/"&gt;inadequately supported by academia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The next weekend, on the Caltrain to the &lt;a href="https://computerhistory.org/"&gt;Computing History
Museum&lt;/a&gt;, we worked on choosing a name.
After playing around with some terrible acronyms, we
decided to continue the theme of Pacific islands begun by Fiji (though
Fiji itself is a spectacular recursive acronym, “Fiji Is Just ImageJ”).
We needed a starting point, and we settled on the
geographic midpoint between Loïc's base of San Francisco and mine of Melbourne.
That's in the middle of the ocean, of course, but not too far from
the tiny village of Napari, in the Republic of Kiribati. We
thought it had a nice ring to it, and the name stuck.&lt;/p&gt;
&lt;p&gt;There was a big risk of napari never getting off the ground. Starting a new lab
is hard work, and Loïc
knew he would not have time to develop it further. He did, however, have some
funds available for a summer intern, and after seeing the work Kira did that
week, he offered her the internship. (As a result, Kira is now officially a
college dropout and full-time software engineer at CZI, the Chan Zuckerberg
Initiative.) That summer, under the guidance of Loïc, Stéfan van der Walt, and
myself, Kira put together the first implementation of napari as you see it
today.&lt;/p&gt;
&lt;p&gt;By late summer/early fall, although internally napari was quite a mess, as
tends to happen in new, fast-growing projects, functionally it was already
impressive enough that Jeremy Freeman and Nick Sofroniew, from CZI's
Computational Biology division, started paying close attention. Nick, who'd
had similar experiences to mine working with Python and nD images, fell in
love with it, and literally could not wait for us to move it forward. He took
matters into his own hands.&lt;/p&gt;
&lt;p&gt;Initially, Nick brought some brilliant management to napari, instituting weekly
meetings that he drove (and continues to drive) like a champion. Those meetings
were critical to bringing others into the loop, including Ahmet Can Solak, Kevin
Yamauchi, Bryant Chunn, and Shannon Axelrod, who started to make pull requests
to improve napari.  Pretty soon, though, Nick made his own pull request, and
after that it was like seeing a racecar take off.  No one has done more for
napari than Nick, and Loïc and I owe him an enormous debt of gratitude.&lt;/p&gt;
&lt;p&gt;Soon after that, the &lt;a href="https://github.com/spacetx/starfish"&gt;StarFISH team&lt;/a&gt; at
CZI adopted napari as their main image viewer, which again helped us
discover bugs, improve our UI, and add features. By February, everyone in the
project was using it regularly, despite its warts. We started to think about
broadening the group of people with eyes on napari.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://ilovesymposia.com/napari/nick-neurodegen-meeting.jpg" alt="Nick demoing napari" style="width:100%"&gt;
  &lt;figcaption&gt;&lt;em&gt;Nick shows napari to attendees of the &lt;a href="https://medium.com/@cziscience/building-a-scientific-community-that-is-more-than-the-sum-of-its-parts-ffc0171617a5"&gt;Neurodegeneration Challenge Network Investigators Meeting&lt;/a&gt; in Februrary. &lt;a href="https://twitter.com/cziscience/status/1101005066599723009"&gt;Photo by CZI Science.&lt;/a&gt;&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;CZI was organising a meeting for grantees of its &lt;a href="https://go.chanzuckerberg.com/imaging"&gt;Imaging
Scientists&lt;/a&gt; and &lt;a href="https://go.chanzuckerberg.com/imaging#imaging-software-fellows"&gt;Imaging Software
Fellows&lt;/a&gt;
programs in March, which we thought would be a good opportunity to grow the
team. Among others, we invited John Kirkham (NVIDIA) and Eric Perlman (then at
Johns Hopkins, now freelancing). The CZI meeting was a dramatic demonstration of the power of
bringing together people with complementary experience, as John added support
for viewing bigger-than-RAM &lt;a href="https://docs.dask.org/en/stable/array.html"&gt;dask
arrays&lt;/a&gt; in about 20 minutes flat,
and Eric massively improved the performance of our segmentation visualisation
layer.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://ilovesymposia.com/napari/loic-napari-master-class.jpg" alt="Loïc explains napari" style="width:100%"&gt;
  &lt;figcaption&gt;&lt;em&gt;Loïc explaining napari's design to John Kirkham and Wei Ouyang at the Chan Zuckerberg Biohub the day after the CZI meeting.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I came out of the March meeting thinking hey, this napari thing might have legs!
Not only had newcomers been able to improve it quickly without knowing
the codebase (which usually points to at least &lt;em&gt;decent&lt;/em&gt; design), but we heard
over and over from the imaging scientists that viewing and annotating large
datasets remained a challenge for them.&lt;/p&gt;
&lt;p&gt;So, we started gently expanding the user base, but in hushed tones, full of
caveats: “This software we're working on might be useful for you… If you're
brave and can work around all the missing functionality… Be sure to report any
issues…” One of our most frequent requests, which was indeed part of the napari
plan from the very beginning, was 3D volume rendering, and Pranathi Vemuri, a
data scientist at the Biohub, implemented that just in time for SciPy 2019. At
that conference, Nick presented napari to the public for the first time,
because many SciPy attendees tend to not mind working with development versions
of libraries. Indeed, we got contributions that week from Alex de Siqueira
(from the scikit-image team) and Matthias Bussonnier (IPython).&lt;/p&gt;
&lt;p&gt;The biggest revelation for me, though, came immediately after SciPy, when I
went to Jackson Lab to teach a workshop on scikit-image. I've taught
scikit-image many times before, but for the first time, I went off-script and
analysed new data live, on IPython, rather than using Jupyter notebooks
pre-populated with toy data. I used napari instead of matplotlib, and found
napari's layers model &lt;em&gt;insanely&lt;/em&gt; useful to visualise every step of the
analysis, one on top of the other, toggling their visibility on and off to
compare them. And it was super easy to add interactivity to the mix, clicking
on some points in napari, then grabbing those points to use as watershed seeds,
and popping the resulting segmentation as another layer to napari.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-annotated-coins.png" alt="Annotated Coins" style="width:100%"&gt;
  &lt;figcaption&gt;&lt;em&gt;Combining interactive annotation and segmentation algorithms with napari.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the months since then, we've spent a lot of time improving the code, and
polishing the little edge cases, like making sure the coordinates reported by
the cursor are pixel perfect, merging the previously separate Image (2D) and
Volume (3D) layers, and extending 3D and multi-resolution support to all
layers.&lt;/p&gt;
&lt;p&gt;Thanks to CZI's massive in-house investment in open source, we also actually
got an &lt;a href="https://github.com/napari/napari/issues/469"&gt;audit&lt;/a&gt; for napari's GUI
from Lia Prins, a UX designer at CZI — an incredible opportunity that few
academic projects get. And, to repeat a familiar pattern, Nick immediately got
to work and implemented a bunch of fixes from the audit. To me, this update is
what's most pushed me to write this post, as the UI now feels more like a
coherent whole than the mishmash of features that results from organic growth
of a project.&lt;/p&gt;
&lt;h2&gt;napari now&lt;/h2&gt;
&lt;p&gt;As I mentioned at the start, currently, everyone on the team uses napari on a
daily basis for their own work. Our workflows actually look quite
different, yet napari meets the needs of all of them, in some form or another.
Last month we were surprised to find that Constantin Pape, at EMBL, was
developing his own viewer, Heimdall, on top of napari, completely independently
from us. I'm a big fan of Constantin's work, so little has brought me more joy
than finding his &lt;a href="https://github.com/constantinpape/heimdall"&gt;repo&lt;/a&gt; and reading
this comment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;most of the credit goes to napari; it's so great to finally have a decent
python viewer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, what are those use cases?&lt;/p&gt;
&lt;h3&gt;1. Just &lt;em&gt;looking&lt;/em&gt; at NumPy ndarrays quickly&lt;/h3&gt;
&lt;p&gt;This is my most common use case. I like to think about and use 2D, 3D, and 4D
images interchangeably. So, I often find myself calling &lt;code&gt;plt.imshow&lt;/code&gt; on a 3D
array and being greeted by a &lt;code&gt;TypeError: Invalid dimensions for image data&lt;/code&gt;,
&lt;em&gt;after&lt;/em&gt; the figure window has popped up, which now stares back at me blank and
forlorn. No more, with &lt;code&gt;napari.view_image&lt;/code&gt;. By default, napari will display the
last two dimensions of an array, and put in as many sliders as necessary for
the remaining dimensions.&lt;/p&gt;
&lt;p&gt;To illustrate, I'll create a syntethic 4D array of blobs using scikit-image's
&lt;code&gt;binary_blobs&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;


&lt;span class="n"&gt;blobs_raw&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;stack&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binary_blobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_dim&lt;/span&gt;&lt;span class="o"&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;volume_fraction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;f&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;f&lt;/span&gt; &lt;span class="ow"&gt;in&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;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&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="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;blobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaussian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobs_raw&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;2&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;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobs&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;/pre&gt;


&lt;pre class="code literal-block"&gt;(10, 256, 256, 256)
&lt;/pre&gt;


&lt;p&gt;Now we can look at the volume in napari:&lt;/p&gt;
&lt;pre class="code literal-block"&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;view_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-blobs-720p.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-blobs-720p.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-blobs-720p-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;Thanks to VisPy and OpenGL, the canvas is just
blazing fast, and a joy to navigate.&lt;/p&gt;
&lt;p&gt;Using napari, we can immediately see that the blobs grow dramatically along
the leading axis. With my previous matplotlib workflow, I might have missed
that, as I would only look at one or two slices before continuing my workflow
in whatever preconceived direction I'd chosen.&lt;/p&gt;
&lt;p&gt;We can click on the little cube icon to switch to a 3D view (or type
&lt;code&gt;viewer.dims.ndisplay = 3&lt;/code&gt; in our IPython terminal). Napari will remove
one of the sliders, and display a maximum intensity projection of the volume.&lt;/p&gt;
&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-blobs-3d-720p.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-blobs-3d-720p.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-blobs-3d-720p-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;White blobs turn out to not look so great in maximum intensity, so let's look
at the 3D view with the famous
&lt;a href="http://fiji.sc/downloads/Spindly-GFP.zip"&gt;mitosis dataset&lt;/a&gt; from Fiji's sample
data (which I think comes from
&lt;a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2064361/"&gt;this paper&lt;/a&gt; by Griffis,
Stuurman and Vale, but I'm not sure...):&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;

&lt;span class="n"&gt;mitosis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&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="s"&gt;'mitosis.tif'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# make sure your working directory is right&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mitosis&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;/pre&gt;


&lt;pre class="code literal-block"&gt;(51, 5, 2, 196, 171)
&lt;/pre&gt;


&lt;p&gt;Those axes are time, z, channels, y, and x (TZCYX).&lt;/p&gt;
&lt;pre class="code literal-block"&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;view_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mitosis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'mitosis'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-mitosis-720p.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-mitosis-720p.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-mitosis-720p-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;As above, you can see the last two dimensions displayed, then sliders for the
remaining dimensions. The sliders are proportionally sized to the number of
slices along each axis, making it easy to figure out which axis is what. (This
was one of Lia's recommendations that Nick implemented.) (And yes, labeled axes
are in our roadmap!)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit: 2019-10-28:&lt;/strong&gt; &lt;em&gt;removed broken use of &lt;code&gt;view_multichannel&lt;/code&gt; function.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We want to see the two channels as individual images to overlay, rather than as
an extra dimension.  We can do this manually with two &lt;code&gt;add_image&lt;/code&gt; calls, adding
a scale keyword argument to account for the different scales in xy and in z:&lt;/p&gt;
&lt;pre class="code literal-block"&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;mitosis&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="p"&gt;:,&lt;/span&gt; &lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'magenta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'aurora-B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&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;10&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;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;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6500&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;mitosis&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="p"&gt;:,&lt;/span&gt; &lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'tubulin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale&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;10&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;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;1600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-mitosis-3d-720p.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-mitosis-3d-720p.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-mitosis-3d-720p-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;2. Overlaying computation results&lt;/h3&gt;
&lt;p&gt;As I mentioned earlier, the first time it really hit me how useful napari could
be was when I was teaching scikit-image at Jackson lab. Napari uses a layered
display model, so you can keep adding layers on top of each other, and change
their opacity or visibility on the fly. This makes it really convenient to
examine the steps of a pipeline as you process data, and maybe try different
methods if the results don't look so great mid-pipeline.&lt;/p&gt;
&lt;p&gt;In this example, I open napari from IPython and I keep adding layers as I try
different segmentation methods on the coins example image from scikit-image:&lt;/p&gt;
&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-coins-pipeline.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-coins-pipeline.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-coins-pipeline-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;3. Annotating data&lt;/h3&gt;
&lt;p&gt;Sometimes, it can be difficult to get an algorithm to pick out exactly what
you want in an image. With the right UI, however, annotation can be extremely
fast, and just a little interaction can dramatically help automated algorithms.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;segmentation&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;morphology&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;napari&lt;/span&gt;


&lt;span class="n"&gt;coins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coins&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;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'coins'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;edges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sobel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;edges_layer&lt;/span&gt; &lt;span class="o"&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;edges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'magenta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'additive'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pts_layer&lt;/span&gt; &lt;span class="o"&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_points&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pts_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'add'&lt;/span&gt;
&lt;span class="c"&gt;# annotate the background and all the coins, in that order&lt;/span&gt;

&lt;span class="n"&gt;coordinates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pts_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;coordinates_int&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;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;markers_raw&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;zeros_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;markers_raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coordinates_int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&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="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="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;# raw markers might be in a little watershed "well".&lt;/span&gt;
&lt;span class="n"&gt;markers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;morphology&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dilation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markers_raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;morphology&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disk&lt;/span&gt;&lt;span class="p"&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;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;segmentation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;watershed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;labels_layer&lt;/span&gt; &lt;span class="o"&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_labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;segments&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="c"&gt;# make background 0&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-annotate-coins.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-annotate-coins.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-annotate-coins-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;4. Viewing very large (dask) arrays&lt;/h3&gt;
&lt;p&gt;Thanks to John Kirkham's work, napari only loads the data that
it needs to display. Therefore, virtual arrays such as Dask arrays, HDF5
datasets, and zarr files can be loaded quickly and easily into napari, provided
that the individual slices are small enough.&lt;/p&gt;
&lt;p&gt;Today's microscopes produce datasets ranging in the hundreds of GB to multiple
TB in size. Berkeley's Gokul Upadhyayula has made his lattice light sheet data
publicly available. Nick converted this to a
&lt;a href="https://zarr.readthedocs.io/en/stable/"&gt;zarr file&lt;/a&gt; for ease of access. &lt;/p&gt;
&lt;p&gt;Now, we can use a dask array to view this 100GB array quickly and easily:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;da&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;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="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'~/data/gokul-lls/aollsm-m4-560nm.zarr'&lt;/span&gt;&lt;span class="p"&gt;)&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;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_image&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'560nm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'magma'&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;150&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-gokul-lls.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-gokul-lls.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-gokul-lls-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;5. Quickly looking at images&lt;/h3&gt;
&lt;p&gt;When you &lt;code&gt;pip install napari&lt;/code&gt;, you also get a command-line client that lets
you quickly view image stacks. Here is a
&lt;a href="https://ilovesymposia.com/data/droso-ovarioles-isotropic.tif"&gt;downsampled, isotropic version&lt;/a&gt; of
&lt;a href="https://figshare.com/articles/_/9985568"&gt;this image&lt;/a&gt; from my colleagues,
Volker Hilsenstein and André Nogueira Alves:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;napari ~/data/ovarioles/droso-ovarioles-isotropic.tif&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-ovarioles.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-ovarioles.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-ovarioles-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;Or the &lt;a href="https://samples.scif.io/EmbryoCE.zip"&gt;4D C. elegans embryo&lt;/a&gt; from the
&lt;a href="https://scif.io/images/"&gt;scif.io example images&lt;/a&gt; (unzipping gives a folder of
images):&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;napari ~/data/EmbryoCE&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-embryo-ce.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-embryo-ce.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-embryo-ce-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;Or a data set of many images, such as the red blood cell spectrin network from
my
&lt;a href="https://peerj.com/articles/4312/"&gt;work with Adam Blanch in Leann Tilley's lab&lt;/a&gt;
(data available from the &lt;a href="https://osf.io"&gt;Open Science Framework&lt;/a&gt;
&lt;a href="http://dx.doi.org/10.17605/OSF.IO/SVPFU"&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;napari ~/data/schizonts/*.tif&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-schizonts.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-schizonts.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-schizonts-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;6. Parameter sweeps&lt;/h3&gt;
&lt;p&gt;A hat tip to Davis Bennett at Janelia, who
&lt;a href="https://gist.github.com/d-v-b/5dbdb7513d07e3360cbe5c0f3d5cacb6"&gt;came up&lt;/a&gt;
with this one. Suppose you want to manually find the best threshold for an
image. Thanks to dask's lazy evaluation, you can attach a function call to a
dimension slider, and use that slider to call the function with many
different parameter choices. And napari's dimensions model tries to mirror
NumPy's
&lt;a href="https://numpy.org/devdocs/user/theory.broadcasting.html"&gt;broadcasting rules&lt;/a&gt;,
so that you can view data of different dimensionality together.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dask&lt;/span&gt;


&lt;span class="n"&gt;coins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;threshold&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;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;arr&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;image&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="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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;

&lt;span class="n"&gt;all_thresholds&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;stack&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&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;t&lt;/span&gt; &lt;span class="ow"&gt;in&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;255&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;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'coins'&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;all_thresholds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'thresholded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'magenta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'additive'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;We can even have a bit of fun at the end with 3D rendering... 🙃&lt;/p&gt;
&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-coins-thresholding.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-coins-thresholding.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-coins-thresholding-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;This is not just a cute demo. I actually found this trick
&lt;a href="https://github.com/scikit-image/scikit-image/issues/4194#issuecomment-537420178"&gt;really useful&lt;/a&gt;
when investigating a bug in scikit-image. By varying the threshold
for h-maxima, I could see detections blinking in and out of existence, even
though they are supposed to only decrease as the parameter value is increased,
thus confirming that there is a bug in the scikit-image implementation of
h-maxima! 😬&lt;/p&gt;
&lt;p&gt;And indeed, improving that example for this post, the broadcasting works with
points, too, not just images!&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;image&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;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'heatmap.npy'&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;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_image&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;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'original'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;blending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'additive'&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;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="n"&gt;hmaxima&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;f&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;f&lt;/span&gt; &lt;span class="ow"&gt;in&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;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;points&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;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nonzero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'maxima coordinates'&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opacity&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;result_image&lt;/span&gt; &lt;span class="o"&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colormap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'magenta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;blending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'additive'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-hmaxima-bug.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-hmaxima-bug.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-hmaxima-bug-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;h3&gt;7. Overlay polygons&lt;/h3&gt;
&lt;p&gt;One of Nick's earliest contributions was one of his biggest: a vector-based
"shapes" layer for polygons, rectangles, circles, and other annotations. This
makes it very easy to replicate things like the &lt;a href="https://scikit-image.org/docs/dev/auto_examples/edges/plot_active_contours.html"&gt;active
contours&lt;/a&gt;
scikit-image example, without having to flip coordinates, because napari is an
image-first library:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage.color&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rgb2gray&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;skimage.segmentation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;active_contour&lt;/span&gt;


&lt;span class="n"&gt;astro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astronaut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;astro_gray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rgb2gray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;astro&lt;/span&gt;&lt;span class="p"&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;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linspace&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="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;pi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&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;sin&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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&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;cos&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;init&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="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;

&lt;span class="n"&gt;snake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;active_contour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gaussian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;astro_gray&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;init&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.015&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="o"&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;gamma&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'rc'&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;napari&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;astro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'astro'&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_shapes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'initial'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;face_color&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;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;edge_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
                  &lt;span class="n"&gt;shape_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'polygon'&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_shapes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'snake'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;face_color&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;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;edge_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
                  &lt;span class="n"&gt;shape_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'polygon'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;&lt;img alt="active contours" src="https://ilovesymposia.com/napari/astronaut-snake.png"&gt;&lt;/p&gt;
&lt;h3&gt;8. More!&lt;/h3&gt;
&lt;p&gt;Nick has been working hard to improve our
&lt;a href="https://github.com/napari/napari-tutorials"&gt;tutorials&lt;/a&gt;, which will have more
details about all of the above. Meanwhile, Ahmet Can and Bryant are busy making
it easier to use napari to view images live from a microscope, or another
source. We look forward to hearing more use cases from you! Your first point of
contact to ask for help should be the &lt;a href="https://forum.image.sc"&gt;image.sc forum&lt;/a&gt;
— don't forget to use the "napari" tag!  — as well as our &lt;a href="https://github.com/napari/napari"&gt;GitHub
issues&lt;/a&gt; for bug reports and feature requests.&lt;/p&gt;
&lt;p&gt;This might be a good time to emphasise that &lt;strong&gt;napari is still in alpha&lt;/strong&gt;, which
means that we might change the API at any time. This is not to discourage you
from using it, but rather to point out that you should be ready to evolve
quickly with it, or pin to the current minor version.&lt;/p&gt;
&lt;h2&gt;Our vision for the future&lt;/h2&gt;
&lt;p&gt;At the risk of sounding like a broken record, napari has already proven itself
insanely useful for all of us at the team, as well as a few extended members of
our community. But we are just getting started. We want to napari to help not
just Python practitioners, but also biologists and other scientists who want to
access Python's enormous scientific ecosystem, but don't necessarily want to
learn Python.&lt;/p&gt;
&lt;p&gt;We are &lt;a href="https://github.com/napari/napari/pull/263"&gt;working&lt;/a&gt; on a plugin system
to allow the data from napari layers to be the input to Python functions, and
have their outputs appear as new layers. A sneak peek:&lt;/p&gt;
&lt;video width="90%" autoplay loop muted playsinline&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-plugins-720p.mp4" type="video/mp4"&gt;&lt;/source&gt;
  &lt;source src="https://ilovesymposia.com/napari/napari-plugins-720p.ogg" type="video/ogg"&gt;&lt;/source&gt;
  &lt;img src="https://ilovesymposia.com/napari/napari-plugins-720p-frame.jpg" title="Your browser does not support the video tag."&gt;
&lt;/video&gt;

&lt;p&gt;We are building this in
&lt;a href="https://github.com/napari/napari/issues/140"&gt;collaboration&lt;/a&gt; with the
developers of &lt;a href="https://imjoy.io/"&gt;ImJoy&lt;/a&gt; to make our plugins cross-compatible.&lt;/p&gt;
&lt;p&gt;In addition to applying functions and seeing the outputs (what ImJoy's Wei
Ouyang refers to as &lt;em&gt;functional&lt;/em&gt; plugins), we see napari as a basis for more
complex interactivity, such as closed-loop learning in the style of
&lt;a href="https://www.ilastik.org/"&gt;Ilastik&lt;/a&gt;, skeleton tracing, 3D annotation, and more.
We already provide a framework to add or modify keyboard shortcuts or
mouse interactivity, so it's possible to write this functionality on top of
napari &lt;em&gt;now&lt;/em&gt;. But we want to provide a &lt;em&gt;unified&lt;/em&gt; framework that will prevent
plugins from clobbering each other's functionality.&lt;/p&gt;
&lt;p&gt;Our aim for the plugin framework is for every function to produce
valid and &lt;em&gt;readable&lt;/em&gt; Python code, so that path from performing a set of
operations to producing a Python module — and thus a plugin — is perfectly
smooth.&lt;/p&gt;
&lt;p&gt;At the March CZI meeting, someone asked me whether we would
provide a headless mode for napari processing. Yes and no: Our motto developing
the plugin system is that &lt;em&gt;napari-headless is just Python.&lt;/em&gt;
With the recording capability and built-in IPython console, we think that
napari could be a platform to teach
non-computational scientists the basics of Python, thus providing an
accelerated loop converting scientists from users to plugin
contributors, and then to library contributors, and improving the whole
ecosystem in the process.&lt;/p&gt;
&lt;h2&gt;Join us!&lt;/h2&gt;
&lt;p&gt;In a little over a year, napari has grown beyond what I thought possible. But
we still have a lot of work to do! In addition to our &lt;a href="https://github.com/napari/napari/issues"&gt;issues
list&lt;/a&gt;, we have an &lt;a href="https://github.com/napari/napari/issues/420"&gt;open
roadmap&lt;/a&gt;. Feel free to jump in and
join the fray!&lt;/p&gt;
&lt;p&gt;We also encourage you to contribute to the many open source projects on which we
depend, starting with VisPy: if you have OpenGL experience, they could really
use your help! Other libraries we build on include
NumPy, SciPy, IPython, QtConsole, ImageIO, scikit-image, QtPy, and PyOpenGL.&lt;/p&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;I've tried to name people's contributions to napari as they happened, but so
many have helped to build it, it was impossible to name everyone. Our
ever-growing &lt;a href="https://github.com/napari/napari/graphs/contributors"&gt;committers list&lt;/a&gt;
can be found on GitHub, but many more have contributed in countless ways. CZI's
Shannon Axelrod and Ambrose Carr helped us make early design decisions when they
adopted napari as the main viewer for StarFISH. Charlotte Weaver helped us build
a (still upcoming) binary installer, and serves on our &lt;a href="https://github.com/napari/napari/blob/master/docs/CODE_OF_CONDUCT.md"&gt;Code of
Conduct&lt;/a&gt;
(modelled after SciPy's). Greg Johnson and his team at the Allen Institute have
used napari to build a corpus of annotated images — and helped drive
the design of napari for that use case.&lt;/p&gt;
&lt;p&gt;At the Imaging Kickoff Meeting last March, since CZI had invited me, I was
staying at a hotel downtown rather than at Loïc's. One night, Loïc sent me an
exasperated message that still brings a smile to my face: “You know, if you'd
stayed at your stupid hotel a year ago, there would be no napari.” It
constantly amazes me how many things had to line up for napari to exist, let
alone be where it is today.&lt;/p&gt;
&lt;p&gt;I'm so grateful to everyone who has helped napari get to get to this point. If
I haven't mentioned your contribution, please email me and I'll make it right.
One nice thing about blog posts is that they are easy to update!&lt;/p&gt;&lt;/div&gt;</description><category>Fiji</category><category>napari</category><category>NumPy</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>SciPy</category><category>software</category><guid>https://ilovesymposia.com/2019/10/24/introducing-napari-a-fast-n-dimensional-image-viewer-in-python/</guid><pubDate>Thu, 24 Oct 2019 13:59:54 GMT</pubDate></item><item><title>Why citations are not enough for open source software</title><link>https://ilovesymposia.com/2019/05/28/why-citations-are-not-enough-for-open-source-software/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;A few weeks ago I wrote about &lt;a href="https://ilovesymposia.com/2019/05/02/why-you-should-cite-open-source-tools/"&gt;why you should cite open source
tools&lt;/a&gt;.
Although I think citations important, though, there are major problems in
relying on them &lt;em&gt;alone&lt;/em&gt; to support open source work.&lt;/p&gt;
&lt;p&gt;The biggest problem is that papers describing a software library can only give
credit to the contributors at the time that the paper was written. The
preferred citation for the SciPy library is “Eric Jones, Travis Oliphant, Pearu
Peterson, &lt;em&gt;et al&lt;/em&gt;”, 2001. The “&lt;em&gt;et al&lt;/em&gt;” is not an abbreviation here, but a
fixed shorthand for all other contributors. Needless to say many, &lt;em&gt;many&lt;/em&gt; people
have contributed to the SciPy library since 2001 (GitHub counts 716
contributors as of this writing), and they are unable to get credit within the
academic system for those contributions. (As an aside, Google counts about
1,200 citations to SciPy, which is a breathtaking undercounting of its value
and influence, and reinforces my earlier point: &lt;strong&gt;cite open source software!
Definitely don't use this post as an excuse not to cite it!!!&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;Not surprisingly, we have had massive contributions to scikit-image since our
2014 paper, and those contributors miss out on the citations to our paper.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;From a maintainer point of view, one way to deal with this is to periodically
write “update” publications that become the preferred way to cite the
software. This is the approach taken by the
&lt;a href="https://cellprofiler.org/citations/"&gt;CellProfiler&lt;/a&gt; team, for example, and the
one we will take for scikit-image. This allows new contributors to benefit,
while also preventing infinite returns for the original authors, which is as
it should be.&lt;/p&gt;
&lt;p&gt;I think this is as good as project maintainers can do now, but leaves open the
questions of authorship, publication frequency, and the potential dilution of
the authors' message.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;proper&lt;/em&gt; fix requires structural change that directly recognises the value of
open source software to science. Software papers are in fact a &lt;em&gt;hack&lt;/em&gt; of the
academic system, a currency conversion, and as with any conversion, there are
losses and inefficiencies involved. Indeed, the paper provides less value than
good documentation for the software. (Last year, when someone tweeted about their
latest software paper, many replies asked for the GitHub link in the
abstract! People wanted to look at the software, not the paper.)&lt;/p&gt;
&lt;p&gt;So what does open source software &lt;em&gt;really&lt;/em&gt; need, if not citations? Of course,
it's money. Citations are useful because they can &lt;em&gt;potentially&lt;/em&gt; be translated
into grants, promotions, and jobs, but wouldn't it be great if we could cut out
the middleman and just have great open source translate &lt;em&gt;directly&lt;/em&gt; into
grants, promotions, and jobs?&lt;/p&gt;
&lt;p&gt;The insane thing is that granting bodies actually give enormous amounts of
money to &lt;em&gt;closed source&lt;/em&gt; software developers, since grant money is routinely
spent on software licenses. Instead of subsidising proprietary software, that
money could be used to fund open source developers, and improve software for
&lt;em&gt;everyone&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;How to fund open source&lt;/h3&gt;
&lt;p&gt;I might have left the how for a later post, but while I was drafting this, CZI
&lt;a href="https://twitter.com/cziscience/status/1128693937130991623"&gt;announced&lt;/a&gt; direct
funding specifically for open source software. Needless to say I think this is
wonderful. I am funded by an earlier closed
&lt;a href="https://www.chanzuckerberg.com/newsroom/czi-announces-support-for-open-source-software-efforts-to-improve-biomedical-imaging"&gt;funding round&lt;/a&gt;
to develop scikit-image, and I thought a lot at the time about all the other
deserving, unfunded projects that I use. An open call is absolutely the right
thing to do, and supporting &lt;em&gt;maintenance&lt;/em&gt; rather than development of hot new
things, as this CZI call is doing, is also the right thing to do. (Others have
flagged that a single year of support, even if renewable, makes it difficult to
support careers, and I agree, but this is a new space, and it makes sense that
CZI would dip its toes before jmuping in.)&lt;/p&gt;
&lt;p&gt;The response to CZI's announcement has been absolute fire. There is enormous
pent-up need and
&lt;a href="https://twitter.com/amuellerml/status/1117455802598662144"&gt;frustration&lt;/a&gt; over
funding
&lt;a href="https://twitter.com/story645/status/1117567608222564353"&gt;maintenance and improvement&lt;/a&gt;
for open source tools
&lt;a href="https://twitter.com/jnuneziglesias/status/1131080022750519296"&gt;underpinning&lt;/a&gt;
an enormous amount of science. (Even a month later, reading Andreas's tweet
makes me want to scream in frustration in the crowded train in which I write
this. Not impactful enough!?) Given the buzz around CZI's call, I wonder
whether CZI has enough staff to even read through the avalanche of applications
it will receive. My hope is that national funding bodies will take notice and
start funding open source maintenance work, as Germany has
&lt;a href="https://www.dfg.de/en/research_funding/programmes/infrastructure/lis/funding_opportunities/call_proposal_software/"&gt;recently done&lt;/a&gt;.
(It's unclear to me whether Germany has repeated that call since 2016. If you
know, please leave a comment below!)&lt;/p&gt;
&lt;p&gt;Since I'm an academic, for a long time I was stuck in the mindset that granting
bodies need to step up and recognise that &lt;em&gt;established&lt;/em&gt; open source is a
valuable thing to fund, and this is certainly true. More recently though, as I
dipped my toes into some private sector consulting, it occurred to me that the
situation is completely ridiculous — “Hey look at this incredible thing we
built, it's serving thousands to millions of scientists, can we get some money
to keep it going?” “It's done and it's free, why would we give you money?” It's
a fundamental misunderstanding at the very top of funding agencies of how
software maintenance works.&lt;/p&gt;
&lt;p&gt;There are plenty of people that &lt;em&gt;do&lt;/em&gt; understand the value of the software,
though: its users, many of whom are in a position to use grant money to
help maintain it. But open source developers have not thus far made it
easy to &lt;del&gt;donate money to&lt;/del&gt; invest in their projects. I'm not talking about a
little donate button on the homepage, which will never substantially support
a project. Rather, open source projects should &lt;em&gt;sell&lt;/em&gt; a product, be it a
maintenance contract, a support contract, or a new feature development, or even
a &lt;em&gt;feel-good&lt;/em&gt; contract. But “buying” an open source software package should
feel exactly like buying closed-source software, and to University purchasing
departments it should look exactly like a normal software purchase.&lt;/p&gt;
&lt;p&gt;As I was thinking about this idea, I moved my blog from wordpress.com, for
which I was paying $100 per year, to &lt;a href="https://getnikola.com/"&gt;Nikola&lt;/a&gt;, an
open source static site generator written in Python. I &lt;em&gt;love&lt;/em&gt; Nikola, and was
ready to put my $100 towards the project, but to my surprise, even a donate
button was absent. As Andreas Mueller and Chris Holdgraf have
&lt;a href="https://twitter.com/choldgraf/status/1070672075692613636"&gt;pointed out&lt;/a&gt;,
many projects haven't even &lt;em&gt;considered&lt;/em&gt; what they would do with a large influx
of money if they got one. That needs to change.&lt;/p&gt;
&lt;p&gt;Thankfully, organizations including
&lt;a href="https://mail.python.org/pipermail/numpy-discussion/2019-April/079326.html"&gt;NumFOCUS&lt;/a&gt;,
&lt;a href="https://www.quansight.com/open-source-support"&gt;QuanSight&lt;/a&gt;,
&lt;a href="https://tidelift.com/"&gt;Tidelift&lt;/a&gt;, and others are working hard to rectify
this situation. I'm trying to help where I can and I really look forward to
seeing what they come up with.&lt;/p&gt;
&lt;p&gt;If you are interested in this topic, we are
&lt;a href="https://github.com/numfocus/scipy-2019-funding-foss-bof"&gt;organising&lt;/a&gt; a Birds
of a Feather session to take place at the
&lt;a href="https://www.scipy2019.scipy.org/"&gt;SciPy 2019&lt;/a&gt; in Austin in July.
Please join us!&lt;/p&gt;&lt;/div&gt;</description><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>science</category><guid>https://ilovesymposia.com/2019/05/28/why-citations-are-not-enough-for-open-source-software/</guid><pubDate>Tue, 28 May 2019 08:41:54 GMT</pubDate></item><item><title>Why you should cite open source tools</title><link>https://ilovesymposia.com/2019/05/02/why-you-should-cite-open-source-tools/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;Every now and then, a moment or a sentence in a conversation sticks out at you,
and lodges itself in the back of your brain for months or even years. In this
case, the sentence is a tweet, and I fear that the only way to dislodge it is
to talk about it publicly.&lt;/p&gt;
&lt;p&gt;Last year, I complained on Twitter that a very prominent paper that was getting
lots of attention used scikit-image, but failed to cite our paper. (Or the
papers corresponding to many other open source packages.) I continued that
scientists developing open source software &lt;em&gt;depend&lt;/em&gt; on these citations to
continue their work. (More on this in another post...) One response was that
surely the developers of the open source scientific Python stack were not
scientists per se, and that citations were not a priority for them.&lt;/p&gt;
&lt;p&gt;I still sigh internally when I think of it.&lt;/p&gt;
&lt;p&gt;That tweet manifests a pervasive perception that open source scientific
software is written by God-like figures. These massively experienced software
developers have easy access to funds for their work, and are at the service of
all the other scientists, who are their users. I used to share this perception,
but it is utterly false.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;I certainly hadn't thought about the funding question, but I did think of
packages like NumPy and SciPy as being written by "pros" (whatever that means),
whose main job was to produce these amazing libraries.&lt;/p&gt;
&lt;p&gt;My ideas started to change only after I attended the SciPy 2012 conference, and
I was invited to the scikit-image sprint by its lead author, &lt;a href="https://mentat.za.net/"&gt;Stéfan van der
Walt&lt;/a&gt;. I was totally starstruck, and even after meeting
him I just assumed his job was "open source guru", or somesuch. It is only
later that I learned that he was a postdoctoral researcher and lecturer in
applied mathematics at Stellenbosch University, in South Africa. As with other
academics, his main job was to produce research and to teach. Open source was
something he produced on the side.&lt;/p&gt;
&lt;p&gt;(Years later, as we were scrambling to finish the final chapter for
&lt;a href="http://elegant-scipy.org"&gt;Elegant SciPy&lt;/a&gt;, Stéfan revamped the build
infrastructure for our book — the scripts and configuration files that
converted the Markdown text we were writing to executed code and html. "You
have poor prioritisation skills, you know?", I taunted. He responded: "Yeah.
But a lot of the SciPy documentation toolchain exists because of that." And
yes: the revamped scripts ended up being extremely useful during the editing
phase with O'Reilly.)&lt;/p&gt;
&lt;p&gt;After the conference I continued to contribute to scikit-image, eventually
joining the core development team, but throughout the process I continued to
feel like an &lt;a href="https://en.wikipedia.org/wiki/Impostor_syndrome"&gt;impostor&lt;/a&gt;,
someone who had somehow managed to gain entrance to this hallowed and
otherworldly community despite his inferior skills and knowledge.&lt;/p&gt;
&lt;p&gt;Only after years of interacting with this community did I internalise the fact
that nearly all of this software stack has been produced by practising
scientists who took the extra care and effort to ensure that their code was
robust, well-tested, and easily accessible to all. Despite a recent influx of
interest and contributions from industry, as far as I can tell, most
contributions to the SciPy stack still come from practising scientists in
academia.&lt;/p&gt;
&lt;p&gt;I know this now, but until recently I was suffering from another fallacy: that
if I knew it, clueless as I was, then surely everybody knew it. That tweet
disabused me of that notion, and this is why you're reading this. I hope it is
instructive, and that you'll find it worth sharing widely.&lt;/p&gt;
&lt;p&gt;This idea, that the SciPy stack is made by active scientists, is important
because it affects how this work can be supported. Sadly, neither university
hierarchies nor national funding bodies recognise code as valuable output.
(There are some
&lt;a href="https://twitter.com/ethanwhite/status/1083008449242378240"&gt;exceptions&lt;/a&gt;, but
this remains the norm.) By and large, the only things that count are papers,
grants, and, to a lesser extent, teaching evaluation scores. &lt;/p&gt;
&lt;p&gt;So, yes, many of the original contributors to the SciPy libraries are now in
industry. But they were academics at the time that they contributed and were
driven out because academia did not value their contributions. Academics should
not have to sacrifice their careers to contribute to open source. Citations to
software papers are an imperfect solution to this problem (again, more soon),
but they sure as hell are better than nothing.&lt;/p&gt;
&lt;p&gt;So, if you are a user of open source tools in the Scientific Python stack, I
have two requests for you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When you publish your work, cite every library that you import. Most
   scientific software has a notice on their homepage or README file pointing
   to a paper you can cite. By definition, if you've imported a library, you've
   found it useful, and if you've found it useful, then you probably care about
   supporting its authors. This is a small way you can contribute to their
   success.&lt;/li&gt;
&lt;li&gt;You are good enough to contribute. If you have an issue
   with an open source package you are using, look at the source code. Submit
   an issue to the project's bug tracker (usually GitHub). And try your hand at
   fixing it. The software's authors will usually offer guidance on
   how to do this, and you will improve your own skills as a result. Good
   software development practices is one of the most transferrable skills you
   can gain.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, citations alone will not solve the wider problem that open source
software is chronically undervalued. I have many thoughts about how open source
should be supported, especially in science, but I'll expand on that in an
upcoming post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I'm adding a link to a related post: &lt;a href="http://ilovesymposia.com/2018/06/20/what-do-scientists-know-about-open-source/"&gt;What do scientists know about
open source?&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>science</category><guid>https://ilovesymposia.com/2019/05/02/why-you-should-cite-open-source-tools/</guid><pubDate>Thu, 02 May 2019 02:31:30 GMT</pubDate></item><item><title>Summer school announcement: 2nd Advanced Scientific Programming in Python (ASPP) Asia Pacific!</title><link>https://ilovesymposia.com/2018/08/30/summer-school-announcement-2nd-advanced-scientific-programming-in-python-aspp-asia-pacific/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The Advanced Scientific Programming in Python (ASPP) summer school has had &lt;a href="https://scipy-school.org/archives"&gt;10 successful iterations&lt;/a&gt; in Europe and &lt;a href="https://python.g-node.org/aspp-asia-pacific-2018/"&gt;one iteration here in Melbourne&lt;/a&gt; earlier this year. Another European iteration is starting next week in Camerino, Italy.&lt;/p&gt;
&lt;p&gt;Now, thanks to the generous sponsorship of &lt;a href="https://www.csiro.au"&gt;CSIRO&lt;/a&gt;, and the efforts of &lt;a href="http://biology.anu.edu.au/people/benjamin-schwessinger"&gt;Benjamin Schwessinger&lt;/a&gt; and &lt;a href="https://twitter.com/DataNerdery"&gt;Genevieve Buckley&lt;/a&gt;, two alumni from the Melbourne school, and &lt;a href="https://people.csiro.au/M/K/Kerensa-Mcelroy"&gt;Kerensa McElroy&lt;/a&gt;, Agriculture Data School Coordinator at CSIRO, the Asia Pacific fork of ASPP gets its second iteration in &lt;strong&gt;Canberra&lt;/strong&gt;, &lt;strong&gt;Jan 20-27, 2019&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Key details&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The workshop runs &lt;strong&gt;January 20-27, 2019&lt;/strong&gt; at the &lt;strong&gt;Australian National University&lt;/strong&gt; in Canberra, Australia.&lt;/li&gt;
&lt;li&gt;topics include git, contributing to open source software with github, testing, debugging, profiling, advanced NumPy, Cython, and data visualisation.&lt;/li&gt;
&lt;li&gt;hands-on learning using pair programming&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;free to attend&lt;/strong&gt; (but students are responsible for travel, accommodation, and meals)&lt;/li&gt;
&lt;li&gt;30 student places, to be selected competitively&lt;/li&gt;
&lt;li&gt;application deadline is &lt;strong&gt;Oct 7, 2018&lt;/strong&gt;, 23:59 &lt;a href="https://www.timeanddate.com/time/zones/aoe"&gt;Anywhere On Earth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;website: https://scipy-school.org&lt;/li&gt;
&lt;li&gt;FAQ: https://scipy-school.org/faq&lt;/li&gt;
&lt;li&gt;apply: https://scipy-school.org/applications&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- TEASER_END --&gt;

&lt;h3&gt;Background&lt;/h3&gt;

&lt;p&gt;Three years ago, I had the privilege of teaching the 2015 ASPP school in Munich. It turned out to be a fantastic teaching experience (I have taught in 2 more since), and more importantly, it was a fantastic experience for the students. Students are selected for the school to fit a certain profile, neither too novice nor too advanced. As such, participants selected for the school are almost guaranteed to learn a great deal.&lt;/p&gt;
&lt;p&gt;Indeed, almost every iteration of the school has been co-organised by former students. Sure enough, with the help of two students from the Melbourne instance, we will be able to have a new iteration in Canberra this January.&lt;/p&gt;
&lt;h3&gt;Course description&lt;/h3&gt;

&lt;p&gt;Scientists spend increasingly more time writing, maintaining, and debugging software. While techniques for doing this efficiently have evolved, only few scientists have been trained to use them. As a result, instead of doing their research, they spend far too much time writing deficient code and reinventing the wheel. In this course we will present a selection of advanced programming techniques and best practices that are standard in industry, but especially tailored to the needs of a programming scientist. Lectures are devised to be interactive and to give the students enough time to acquire direct hands-on experience with the materials. Students will work in pairs throughout the school and will team up to practice the newly learned skills in a real programming project — an entertaining computer game.&lt;/p&gt;
&lt;p&gt;We use the Python programming language for the entire course. Python works as a simple programming language for beginners, but more importantly, it also works great in scientific simulations and data analysis. We show how clean language design, ease of extensibility, and the great wealth of open source libraries for scientific computing and data visualization are driving Python to becoming a standard tool for scientists.&lt;/p&gt;
&lt;h3&gt;Who is eligible?&lt;/h3&gt;

&lt;p&gt;This school is targeted at Master/PhD students, postdocs, and academic staff and technicians from all areas of science. Competence in Python or in another language such as Java, C/C++, MATLAB, or Mathematica is absolutely required. Basic knowledge of Python and of a version control system such as git, subversion, mercurial, or bazaar is assumed. Participants without any prior experience with Python and/or git should work through the proposed introductory material before the course.&lt;/p&gt;
&lt;p&gt;We have strived to get a pool of students that is international and gender-balanced, and have succeeded, with gender parity in the last five schools.&lt;/p&gt;
&lt;h3&gt;More questions&lt;/h3&gt;

&lt;p&gt;If you have any questions, contact &lt;a href="mailto:info@scipy-school.org"&gt;info@scipy-school.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please circulate this announcement widely! And follow &lt;a href="https://twitter.com/scipyschool"&gt;@scipyschool&lt;/a&gt; for further developments.&lt;/p&gt;
&lt;p&gt;Juan.&lt;/p&gt;&lt;/div&gt;</description><category>conference</category><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>science</category><guid>https://ilovesymposia.com/2018/08/30/summer-school-announcement-2nd-advanced-scientific-programming-in-python-aspp-asia-pacific/</guid><pubDate>Thu, 30 Aug 2018 04:48:05 GMT</pubDate></item><item><title>The road to scikit-image 1.0</title><link>https://ilovesymposia.com/2018/07/13/the-road-to-scikit-image-1-0/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;This is the first in a series of posts about the joint scikit-image, scikit-learn, and dask sprint that took place at the Berkeley Insitute of Data Science, May 28-Jun 1, 2018.&lt;/p&gt;
&lt;p&gt;In addition to the dask and scikit-learn teams, the sprint brought together three core developers of scikit-image (Emmanuelle Gouillart, Stéfan van der Walt, and myself), and two newer contributors, Kira Evans and Mark Harfouche. Since we are rarely in the same timezone, let alone in the same room, we took the opportunity to discuss some high level goals using a framework suggested by Tracy Teal (via Chris Holdgraf): &lt;em&gt;Vision, Mission, Values&lt;/em&gt;. I'll try do Chris's explanation of these ideas justice:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Vision: what are we trying to achieve? What is the future that we are trying to bring about?&lt;/li&gt;
&lt;li&gt;Mission: what are we going to do about it? This is the &lt;em&gt;plan&lt;/em&gt; needed to make the vision a reality.&lt;/li&gt;
&lt;li&gt;Values: what are we &lt;em&gt;willing&lt;/em&gt; to do, and &lt;em&gt;not&lt;/em&gt; willing to do, to complete our mission?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, on the basis of this framework, I'd like to review where scikit-image is now, where I think it needs to go, and the ideas that Emma, Stéfan, and I came up with during the sprint to get scikit-image there.&lt;/p&gt;
&lt;p&gt;I will point out, from the beginning, that one of our values is that we are &lt;em&gt;community-driven&lt;/em&gt;, and this is not a wishy-washy concept. (More below.) Therefore this blog post constitutes only a preliminary document, designed to kick-start an &lt;em&gt;official roadmap&lt;/em&gt; for scikit-image 1.0 with more than a blank canvas. The roadmap will be debated on GitHub and the mailing list, open to discussion by anyone, and when completed will appear on our webpage. &lt;em&gt;This post is not the roadmap.&lt;/em&gt;&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;h3&gt;Part one: where we are&lt;/h3&gt;

&lt;p&gt;scikit-image is a tremendously successful project that I feel very proud to have been a part of until now. I still cherish the email I got from Stéfan inviting me to join the core team. (Five years ago now!)&lt;/p&gt;
&lt;p&gt;Like many open source projects, though, we are threatened by our own success, with feature requests and bug reports piling on faster than we can get through them. And, because we grew organically, with no governance model, it is often difficult to resolve thorny questions about API design, what gets included in the library, and how to deprecate old functionality. Discussion usually stalls before any decision is taken, resulting in a process heavily biased towards inaction. Many issues and PRs languish for years, resulting in a double loss for the project: a smaller loss from losing the PR, and a bigger one from losing a potential contributor that understandably has lost interest.&lt;/p&gt;
&lt;p&gt;Possibly the most impactful decision that we took at the BIDS sprint is that at least three core developers will video once a month to discuss stalled issues and PRs. (The logistics are still being worked out.) We hope that this sustained commitment will move many PRs and issues forward much faster than they have until now.&lt;/p&gt;
&lt;h3&gt;Part two: where we're going&lt;/h3&gt;

&lt;p&gt;Onto the framework. What are the vision, mission, and values of scikit-image? How will these help guide the decisions that we make daily and in our dev meetings?&lt;/p&gt;
&lt;h4&gt;Our vision&lt;/h4&gt;

&lt;p&gt;We want scikit-image to be &lt;em&gt;the&lt;/em&gt; reference image processing and analysis library for science in Python. In one sense I think that we are already there, but there are more than enough remaining warts that they might cause the motivated user to go looking elsewhere. The vision, then, is to increase our customer satisfaction fraction in this space to something approaching 1.0.&lt;/p&gt;
&lt;h4&gt;Our mission&lt;/h4&gt;

&lt;p&gt;How do we get there? Here is our mission:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our library must be &lt;strong&gt;easily re-usable.&lt;/strong&gt; This means that we will be careful in adding new dependencies, and possibly cull some existing ones, or make them optional. We also want to remove some of the bigger test datasets from our package, which at 24MB is getting rather unwieldy! (By comparison, Python 3.7 is 16MB.) (Props to Josh Warner for noticing this.)&lt;/li&gt;
&lt;li&gt;It also means providing a &lt;strong&gt;consistent API.&lt;/strong&gt; This means that conceptually identical function arguments, such as images, label images, and arguments defining whether an input image is grayscale, should have the same name across various the library. We've made great strides in this goal thanks to Egor Panfilov and &lt;a href="https://github.com/scikit-image/scikit-image/issues/2538"&gt;Adrian Sieber&lt;/a&gt;, but we still have some way to go.&lt;/li&gt;
&lt;li&gt;We want to &lt;strong&gt;ensure accuracy&lt;/strong&gt; of our algorithms. This means comprehensive testing, even against external libraries, and engaging experts in relevant fields to audit our code. (Though this of course is a challenge!)&lt;/li&gt;
&lt;li&gt;Show utmost &lt;strong&gt;care with users' data&lt;/strong&gt;. Not that we haven't cared until now, but there are places in scikit-image where too much responsibility (in my view) rests with the user, with insufficient transparency from our functions for new users to predict what will happen to their data. For example, we are quite liberal with how we deal with input data: it gets rescaled whenever we need to change the type, for example from unsigned 8-bit integers (uint8) to floating point. Although we have &lt;a href="https://github.com/scikit-image/scikit-image/issues/2677#issuecomment-309717979"&gt;good technical reasons&lt;/a&gt; for doing this, and rather &lt;a href="http://scikit-image.org/docs/dev/user_guide/data_types.html"&gt;extensive documentation about it&lt;/a&gt;, these conversions are the source of much user confusion. We are aiming to improve this in &lt;a href="https://github.com/scikit-image/scikit-image/issues/3009"&gt;issue 3009&lt;/a&gt;. Likewise, we don't handle image metadata at all. What is the physical extent of the input image? What is the range and units of the data points in the image? What do the different channels represent? These are all important questions in scientific images, but until now we have completely abdicated responsibility in them and simply ignore any metadata. I don't think this is tenable for a scientific imaging library. We don't have a good answer for how we will do it, but I consider this a must-solve before we can call ourselves 1.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Our values&lt;/h4&gt;

&lt;p&gt;Finally, how do we solve the thorny questions of API design, whether to include algorithms, etc? Here are our values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We used the word "reference" in our vision. This phrasing is significant. It means that &lt;strong&gt;we value elegant implementations&lt;/strong&gt;, that are &lt;em&gt;easy to understand for newcomers&lt;/em&gt;, over obtaining every last ounce of speed. This value is a useful guide in reviewing pull requests. We will prefer a 20% slowdown when it reduces the lines of code two-fold.&lt;/li&gt;
&lt;li&gt;We also used the word &lt;em&gt;science&lt;/em&gt; in our vision. This means our aim is to &lt;strong&gt;serve scientific applications&lt;/strong&gt;, and not, for example, image editing in the vein of Photoshop or GIMP. Having said this, we value being part of diverse scientific fields. (One of the first citations of the scikit-image paper was a remote sensing paper, to our delight: none of the core developers work in that field!)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;We are inclusive.&lt;/strong&gt; From my first contributions to the project, I have received patient mentorship from Stéfan, Emmanuelle, Johannes Schönberger, Andy Mueller, and others. (Indeed, I am still learning from fellow contributors, as seen &lt;a href="https://github.com/scikit-image/scikit-image/pull/3031#issuecomment-398961212"&gt;here&lt;/a&gt;, to show just one example.) We will continue to welcome and mentor newcomers to the Scientific Python ecosystem who are making their first contribution.&lt;/li&gt;
&lt;li&gt;Both of the above points have a corrolary: &lt;strong&gt;we require excellent documentation&lt;/strong&gt;, in the form of usage examples, docstrings documenting the API of each function, and comments explaining tricky parts of the code. This requirement has stalled a few PRs in the past, but this is something that our monthly meetings will specifically address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;We don't do magic.&lt;/strong&gt; We use NumPy arrays instead of fancy façade objects that mask their complexity. We prefer to educate our users over making decisions on their behalf (through quality documentation, particularly in docstrings).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;We are community-driven&lt;/strong&gt;, which means that decisions about the API and features will be driven by our users' requirements, and not the whims of the core team. (For example, I would happily &lt;a href="http://toolz.readthedocs.io/en/latest/curry.html"&gt;curry&lt;/a&gt; all of our functions, but that would be confusing to most users, so I suffer in silence. =P)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope that the above values are uncontroversial in the scikit-image core team. (I myself used to fall heavily on the pro-magic side, but hard experience with this library has shown me the error of my ways.) I also hope, but more hesitantly, that our much wider community of users will also see these values as, well, valuable.&lt;/p&gt;
&lt;p&gt;As I mentioned above, I hope this blog post will spawn a discussion involving both the core team and the wider community, and that this discussion can be distilled into a public roadmap for scikit-image.&lt;/p&gt;
&lt;h3&gt;Part three: scikit-image 1.0&lt;/h3&gt;

&lt;p&gt;I have deliberately left out new features off the mission, except for metadata handling. The library will never be "feature complete". But we &lt;em&gt;can&lt;/em&gt; develop a stable and consistent enough API that adding new features will almost never require breaking it.&lt;/p&gt;
&lt;p&gt;For completeness, I'll compile my personal pet list of things I will attempt to work on or be particularly excited about other people working on. This is &lt;em&gt;not&lt;/em&gt; part of the roadmap, it's part of my roadmap.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Near-complete support for n-dimensional data. I want 2D-only functions to become the exception in the library, maybe so much so that we are forced to add a &lt;code&gt;_2d&lt;/code&gt; suffix to the function name.&lt;/li&gt;
&lt;li&gt;Typing support. I never want to move from simple arrays as our base data type, but I want a way to systematically distinguish between plain images, label images, coordinate lists, and other types, in a way that is accessible to automatic tools.&lt;/li&gt;
&lt;li&gt;Basic image registration functionality.&lt;/li&gt;
&lt;li&gt;Evaluation algorithms for all parts of the library (such as segmentation, or keypoint matching).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The human side&lt;/h3&gt;

&lt;p&gt;Along with articulating the way we see the project, another key part of getting to 1.0 is supporting existing maintainers, and onboarding new ones. It is clear that the project is currently straining under the weight of its popularity. While we solve one issue, three more are opened, and two pull requests.&lt;/p&gt;
&lt;p&gt;In the past, we have been too hesitant to invite new members to the core team, because it is difficult to tell whether a new contributor shares your vision. Our roadmap document is an important step towards rectifying this, because it clarifies where the library is going, and therefore the decision making process when it comes to accepting new contributions, for example.&lt;/p&gt;
&lt;p&gt;In a followup to this post, I aim to propose a &lt;em&gt;maintainer onboarding document&lt;/em&gt;, in a similar vein, to make sure that new maintainers all share the same process when evaluating new PRs and communicating with contributors. A governance model is also in the works, by which I mean that Stéfan has been wanting to establish one for years and now Emmanuelle and I are onboard with this plan, and I hope others will be too, and now we just need to decide on the damn thing.&lt;/p&gt;
&lt;p&gt;I hope that all of these changes will allows us to reach the scikit-image 1.0 milestone sooner rather than later, and that everyone reading this is as excited about it as I was while we hashed this plan together.&lt;/p&gt;
&lt;p&gt;As a reminder, &lt;strong&gt;this is not our final roadmap&lt;/strong&gt;, nor our final &lt;strong&gt;vision/mission statement&lt;/strong&gt;. Please comment on the corresponding &lt;a href="https://github.com/scikit-image/scikit-image/issues/3263"&gt;GitHub issue&lt;/a&gt; for this post if you have thoughts and suggestions! (You can also use the &lt;a href="https://mail.python.org/mailman/listinfo/scikit-image"&gt;mailing list&lt;/a&gt;, and we will soon provide a way to submit anonymous comments, too.) As a community, we will come together to create the library we all want to use and contribute to.&lt;/p&gt;
&lt;p&gt;As a reminder, everything in this blog is &lt;a href="https://dancohen.org/2013/11/26/cc0-by/"&gt;CC0+BY&lt;/a&gt;, so feel free to reuse any or all of it in your own projects! And I want to thank BIDS, and specifically Nelle Varoquaux at BIDS, for making this discussion possible, among many other things that will be written up in upcoming posts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Anonymous comments are now open at https://pollev.com/juannunezigl611. To summarise, to comment on this proposal you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comment on the &lt;a href="https://github.com/scikit-image/scikit-image/issues/3263"&gt;GitHub issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;submit a comment below&lt;/li&gt;
&lt;li&gt;submit an anonymous comment at https://pollev.com/juannunezigl611&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</description><category>image analysis</category><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>software</category><guid>https://ilovesymposia.com/2018/07/13/the-road-to-scikit-image-1-0/</guid><pubDate>Thu, 12 Jul 2018 18:58:35 GMT</pubDate></item><item><title>What do scientists know about open source?</title><link>https://ilovesymposia.com/2018/06/20/what-do-scientists-know-about-open-source/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;A friend recently pointed out this great talk by Matt Bernius, &lt;a href="https://community.redhat.com/blog/2018/05/what-college-students-know/"&gt;What students know and don't know about open source&lt;/a&gt;. If you have even a minor interest in open source it's worth a watch, but the gist is: in the US alone, there are about 200,000 students enrolled in a computer science major. Open source communities are a great space to learn real-world programming, so why don't these numbers translate into massive contributions to open source?&lt;/p&gt;
&lt;p&gt;At the core of the issue, Matt identifies two main problems: (1) colleges and universities simply don't teach open source, or even collaborative coding; and (2), many open source communities make newcomers feel unwelcome in a variety of ways.&lt;/p&gt;
&lt;p&gt;I want to comment about this in the context of programming in science. That is, programming where the code is not the main product, but rather a useful tool to obtain a scientific result, for example in biology or physics. Here, we still see relatively little contribution to open source, for related but different cultural issues.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;I've sent my &lt;a href="https://ilovesymposia.com/2015/12/26/why-scientists-should-code-in-the-open/"&gt;scientists should code in the open&lt;/a&gt; post to a few people and the response from most remains sceptical. I hope this post will address some of their concerns.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Scientific culture is ridiculously secretive&lt;/h3&gt;
&lt;p&gt;The most common objection is to my assertion that people won't scoop you by looking at your code. I remember a tweet (that I sadly can't find now) that really got to the gist of the problem. It went something like this:&lt;/p&gt;
&lt;blockquote&gt;
  Someone in science having a new idea: "Ooh, I hope I don't get scooped!"&lt;br&gt;
  Someone in open source having a new idea: "Ooh, I hope someone has implemented this already!"
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I found the source! It's &lt;a href="https://twitter.com/tweetotaler/status/884412302098915329"&gt;this tweet&lt;/a&gt; by Elizabeth Seiver.&lt;/p&gt;
&lt;p&gt;This is a huge gap in culture that won't soon go away, but there are encouraging steps towards narrowing it. For example, PLOS Biology, a leading journal, recently &lt;a href="http://twitter.com/PLOSBiology/status/958346565868978176"&gt;announced&lt;/a&gt; that they would consider "scooped" studies for publication within six months of the "scooping". That goes some way towards re-aligning incentives towards collaborative and open science.&lt;/p&gt;
&lt;p&gt;I've come across many collaborations that have started because of open source. I have not heard of someone getting scooped because of open source, but of course that sort of information would be hard to trace and come by. Several people did write to me that they were concerned about very specific groups rifling through their code expressly for the purpose of scooping them. For me it's hard to imagine someone even having that attitude, and my advice is that if you do face such a toxic community, it might be wise to change your chosen field of study.&lt;/p&gt;
&lt;p&gt;Nevertheless, I want to emphasise here that open source programming can take many forms, with the zip file attached to the paper being the lowest, coding in the open being the highest, and several other models in between. Any steps you can take towards the higher models will ultimately help you. My preferred mode for code that really does have to be private is to use a private GitHub repository, and &lt;em&gt;just make that repo public once the paper is accepted.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A lot of people prefer the "code dump with no revision history" model of post-publication sharing, but this tosses out a lot of valuable information for people coming after you: what have you tried that didn't work? What issues did you have with the code? Have you considered coding in one style or another? The code dump model also makes you less likely to use GitHub in the first place, depriving you of an opportunity to learn some valuable real-world skills.&lt;/p&gt;
&lt;h3&gt;For coding, scientists have even more severe impostor syndrome&lt;/h3&gt;

&lt;p&gt;As I mentioned in my original post, and this I find completely uncontestable, &lt;em&gt;publishing shitty code is not a bad thing.&lt;/em&gt; &lt;em&gt;Everybody writes bad code,&lt;/em&gt; and nearly everybody knows it. Here's Hadley Wickham, creator of &lt;a href="https://dplyr.tidyverse.org"&gt;dplyr&lt;/a&gt;, &lt;a href="https://tidyr.tidyverse.org"&gt;tidy data&lt;/a&gt;, &lt;a href="https://ggplot2.tidyverse.org"&gt;ggplot2&lt;/a&gt;, among other things; in other words, someone who knows a thing or two about elegant code and about as close as one gets to coding royalty in science:&lt;/p&gt;
&lt;blockquote&gt;
  The only way to write great code is to write lots of shitty code first.
&lt;/blockquote&gt;

&lt;p&gt;Publishing your raw code is a good thing and will absolutely not be a black mark on your career. Indeed, in open source circles, it is often a bare GitHub contribution history that is a black mark. (And this is another problem, but in my opinion a better one.)&lt;/p&gt;
&lt;h3&gt;Scientists don't know about open source&lt;/h3&gt;

&lt;p&gt;If knowledge of open source is lacking in computer science, what chance does it have in other fields? The truth is that outreach and education need to become a massive part of open source culture, &lt;em&gt;especially&lt;/em&gt; in science.&lt;/p&gt;
&lt;p&gt;I credit &lt;a href="https://bids.berkeley.edu/people/st%C3%A9fan-van-der-walt"&gt;Stéfan van der Walt&lt;/a&gt; for my life in open source. After I gave a talk at SciPy 2012, he invited me to join the scikit-image sprint at the end of the conference. If it hadn't been for that, I probably would have just wandered around the hall, too shy to join any sprint (see "impostor syndrome", above), and my life would be very different right now.&lt;/p&gt;
&lt;p&gt;Anyway, at that point I'd made my code "open source", which meant it was on GitHub. I had only added a license to submit to the conference. As a reminder, unlicensed code &lt;a href="http://www.astrobetter.com/blog/2014/03/10/the-whys-and-hows-of-licensing-scientific-code/"&gt;doesn't count as open source&lt;/a&gt;. But I had never really collaborated in open source. My idea of collaboration was my workflow with my colleague: a single branch (master), from which we both pulled and to which we both pushed. When I sat down with Stéfan and &lt;a href="http://tonysyu.github.io"&gt;Tony Yu&lt;/a&gt;, and I figured what I wanted to work on, I asked: "So, should I just push to master, or what?" I still remember, with some embarrassment, the dubious look Stéfan and Tony exchanged, as they silently figured out which of them would introduce this newbie to &lt;a href="https://help.github.com/articles/creating-a-pull-request/"&gt;pull requests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But that's the thing: I shouldn't feel embarrassment. Scientists for the most part don't get introduced to coding in their education, much less to open source.&lt;/p&gt;
&lt;h3&gt;What can scientists in open source do?&lt;/h3&gt;

&lt;p&gt;A lesson from my continued contributions to the SciPy ecosystem, I hope, is that some light mentorship can yield enormous dividends later on. Stéfan and Tony took the time to walk me through the open source contribution process, when they could have dismissively sent me a link to some page explaining it. I'm a big fan of writing good documents for newcomers, but nothing beats a good hand-holding. It's very easy for me to imagine an alternate reality where I had not felt welcome or rewarded by the scikit-image project and my life had not taken this productive turn.&lt;/p&gt;
&lt;p&gt;Continuing on imaginary themes, it is only slightly less plausible that the open source scientific world should be awash with new contributors at every level of science. How do we turn this dream into a reality?&lt;/p&gt;
&lt;p&gt;If you are a scientist and this post is among your first encounters with the term "open source", and you think you might be interested in learning more, here are a few things I recommend, in order of easiest to hardest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read the &lt;a href="https://github.com/elegant-scipy/elegant-scipy/blob/master/markdown/preface.markdown"&gt;preface&lt;/a&gt; and &lt;a href="https://github.com/elegant-scipy/elegant-scipy/blob/master/markdown/epilogue.markdown"&gt;epilogue&lt;/a&gt; of my book with Stéfan and &lt;a href="http://harrietdashnow.com"&gt;Harriet Dashnow&lt;/a&gt;. (Free online!) I feel a bit icky recommending my own book, but why repeat myself? In those chapters I tried to distill my thoughts on joining the SciPy community, which is a fantastic, rewarding space in which to do open source programming as a scientist. I expect many things we wrote generalise well to e.g. &lt;a href="https://www.tidyverse.org"&gt;the tidyverse&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Look for upcoming &lt;a href="https://software-carpentry.org"&gt;software carpentry&lt;/a&gt; workshops near you. These are free two-day programming boot camps to introduce you to computational thinking, and, crucially, to version control with git.&lt;/li&gt;
&lt;li&gt;Go to a &lt;a href="https://conference.scipy.org"&gt;SciPy conference&lt;/a&gt;. I know of SciPy, EuroSciPy, and SciPy India, but I have a vague memory of offshoots in Africa and South America.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are in a boat similar to mine (intermediate/advanced open source contributor in science), and you feel like you would like your work to feel a bit more crowded, I can tell you what I'm going to be doing in response to this talk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign up to deliver (more) software carpentry training (or similar). Getting the word out is the number one thing.&lt;/li&gt;
&lt;li&gt;In software carpentry, emphasise the role of git in collaboration. (I think the official program does not go far enough in this direction, and focuses instead on the initial linear history.)&lt;/li&gt;
&lt;li&gt;If you are located in a university, talk to your CS department to see whether they have any courses in open source development. If not, see whether you can guest lecture in a suitable course to make students aware of the open source opportunities out there.&lt;/li&gt;
&lt;li&gt;Similarly, follow up software carpentry with more advanced sessions on open source collaboration. I gained an enormous fraction of my programming skills from collaborating on open source. I really think there is no better tool for long-term learning in this space. An idea that I'd like to try out is to curate a bunch of open issues on prominent repos and get SWC students to sprint on them for a day&lt;sup id="fnref-1111-1"&gt;&lt;a href="https://ilovesymposia.com/2018/06/20/what-do-scientists-know-about-open-source/#fn-1111-1" class="jetpack-footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;. I know about the "good first issue" tag on GitHub. Unfortunately, my experience with it is mixed. I think many repos are overly optimistic with theirs (this includes scikit-image), and, furthermore, a large proportion of these tagged issues get "claimed" quickly — and often half-heartedly!&lt;/li&gt;
&lt;li&gt;Write, write, write! Did you get a cool PR merged? Write a blog post about it! Or at least tweet! We need to get the message out that writing PRs is for everyone. =)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any further ideas, I'd love to hear them.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;

&lt;li id="fn-1111-1"&gt;
Actually I drafted this post a while back, and tried this yesterday, with mixed success. I'll write about that experience soon. ;) &lt;a href="https://ilovesymposia.com/2018/06/20/what-do-scientists-know-about-open-source/#fnref-1111-1"&gt;↩&lt;/a&gt;
&lt;/li&gt;

&lt;/ol&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</description><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>science</category><guid>https://ilovesymposia.com/2018/06/20/what-do-scientists-know-about-open-source/</guid><pubDate>Wed, 20 Jun 2018 11:19:46 GMT</pubDate></item><item><title>1st ASPP Asia Pacific evaluation survey</title><link>https://ilovesymposia.com/2018/04/09/1st-aspp-asia-pacific-evaluation-survey/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;In January of 2018, we had the first &lt;a href="http://python.g-node.org"&gt;ASPP summer school&lt;/a&gt; outside of Europe. (This was a parallel workshop to the European one, which will be held in Italy in September 2018.) In general, it was a great success, with some caveats that we will elaborate on below.&lt;/p&gt;
&lt;p&gt;First we want to note that this school was a bit different than the European ones, in that we only had attendees from Australian institutions, where the European school has broad international representation, including some from out of Europe. This was in some ways inevitable, as it is more expensive to travel to Australia from almost anywhere than to travel within Europe. On the other hand, we advertised relatively late, and we were unable to secure travel grants during the advertising period, so there is hope that a future edition would be able to attract a more international crowd from the Asia Pacific region.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;Given all this, there was a question as to whether we would be able to capture the atmosphere of the school, which normally sees the students living together and socialising for basically the whole week. In this case, most students just went home after classes were finished. But although some of that atmosphere was missing, by the end of the week we did manage to get some close links between all the students and the faculty. The evaluations below show that most of the value of the school was preserved.&lt;/p&gt;
&lt;p&gt;We note that 100% of the respondents (29/30 of the students) would recommend the course to their peers. So, although some lectures were better received than others, and although the programming project was not universally loved, we managed to provide value for everyone. All of this is in line with the evaluations at previous schools (available at https://python.g-node.org/wiki/archives.html).&lt;/p&gt;
&lt;p&gt;The project, which consists of programming a videogame bot, is controversial every year, but, consistently, more people like it than don't, and people get to practice git, pair programming, and programming as a team, which is the single most difficult skill to practice when programming for science. Indeed when we walk around during the project programming sessions, we see people extremely engaged in what they are coding. It's difficult to imagine a scientific problem engaging such diverse people as the school's attendees (which come from very disparate scientific fields).&lt;/p&gt;
&lt;p&gt;Of all the feedback, two particular statements, we hope from people in the same project group, broke our hearts. We decided not to include them in this report, because they might be easy to de-anonymise by group members, but they boil down to the following: a group member, by being combative and rude to others in their team, and deciding to essentially complete the project by themselves, ruined the programming project for all of their team members, with some even feeling that they were not good enough to contribute. This is tragic, because we want everyone in the school to feel &lt;em&gt;empowered&lt;/em&gt; to do anything at all in Python.&lt;/p&gt;
&lt;p&gt;Absolutely every student has something to offer in this project. Here, as in life, teams are comprised of members of varying skills. But we know from our selection that everyone has the skills to contribute (and this is confirmed by the fact that most attendees, for most lectures, felt that the difficulty level was "just right"). So if a student felt inadequate, it can only be because of the toxic team member.&lt;/p&gt;
&lt;p&gt;Ned Batchelder recently wrote an excellent &lt;a href="https://nedbatchelder.com/blog/201711/toxic_experts.html"&gt;blog post&lt;/a&gt; about what he calls "Toxic experts" and what Tiziano Zito calls, somewhat more bluntly, "Arrogant assholes". (In discussions about this post, Tiziano and others noted that one does not have to be an expert to be toxic, or arrogant, or an asshole. No matter: the points below apply equally to anyone meeting any of the above characteristics &lt;em&gt;regardless&lt;/em&gt; of expertise.)&lt;/p&gt;
&lt;p&gt;The feedback we received should serve as a warning to selection committees and hiring managers everywhere about how damaging it is to allow such a person into your ranks. Due to the anonymous nature of the survey, we can't tell whether there was one or two toxic experts in our midst, but if it's one, they soured the school for five other people. If it's two, then that's ten people, a third of the school, that might have had a terrible experience. The problem with toxic experts is that they can so quickly cause damage to so many others. Thus, even if they are a mythical "10x engineer", &lt;strong&gt;they are not worth it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Literally nothing that the above-described team member could have done, coding-wise, could make up for the damage they caused. Despite their strong opinions, they missed the entire point of the programming project, which is not to win a medal, but to &lt;em&gt;learn about working in a team.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We try to avoid toxic experts in our selection process for the school, but they slip through every so often. In response to this feedback, we will aim to be even more vigilant in our selection, and also make the aims of the project &lt;em&gt;as a learning exercise&lt;/em&gt; more explicit during its introduction. We will also make sure to be more aware of group interactions during the actual school; we apologise to the students involved that we did not catch this behaviour this time. We are truly sorry.&lt;/p&gt;
&lt;p&gt;If you are in the position of being an expert during a school or workshop, don't go it alone. That is a waste of your time, because you can do a programming project on your own whenever you damn well please. Slow down, and think instead about practicing your teaching and mentoring skills. They are also important in life, and, in many contexts, they are your responsibility.&lt;/p&gt;
&lt;p&gt;You can access the full survey results &lt;a href="https://python.g-node.org/wiki/_media/evaluation_survey_2018_melbourne.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;-- Juan, and the Organisers.&lt;/p&gt;&lt;/div&gt;</description><category>conference</category><category>programming</category><category>Python</category><category>science</category><guid>https://ilovesymposia.com/2018/04/09/1st-aspp-asia-pacific-evaluation-survey/</guid><pubDate>Mon, 09 Apr 2018 03:29:53 GMT</pubDate></item><item><title>Summer School Announcement: ASPP Asia-Pacific 2018</title><link>https://ilovesymposia.com/2017/10/05/summer-school-announcement-aspp-asia-pacific-2018/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The Advanced Scientific Programming in Python (ASPP) summer school has had 10 extremely successful iterations in Europe. (You can find past materials, schedules, and student evaluations at &lt;a href="https://python.g-node.org/archives"&gt;https://python.g-node.org/archives&lt;/a&gt;.) Now, thanks to the &lt;a href="https://incf.org"&gt;INCF&lt;/a&gt;, we will be holding its first iteration in Australia, to cater to the Asia Pacific region. (Note: the original ASPP will still take place in Europe next Northern summer; this is a fork of that school.)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Key details&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The workshop runs &lt;strong&gt;January 14-21&lt;/strong&gt; at the &lt;strong&gt;Melbourne Brain Centre&lt;/strong&gt;, University of Melbourne, Australia&lt;/li&gt;
&lt;li&gt;topics include: git, contributing to open source software with github, testing, debugging, profiling, advanced NumPy, Cython, data visualisation.&lt;/li&gt;
&lt;li&gt;hands-on learning using pair programming&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;free to attend&lt;/strong&gt; (but students are responsible for travel, accommodation, and meals)&lt;/li&gt;
&lt;li&gt;30 student places, to be selected competitively&lt;/li&gt;
&lt;li&gt;application deadline is &lt;strong&gt;Oct 31, 2017&lt;/strong&gt;, 23:59 UTC.&lt;/li&gt;
&lt;li&gt;website: https://melbournebioinformatics.org.au/aspp-asia-pacific&lt;/li&gt;
&lt;li&gt;apply: https://melbournebioinformatics.org.au/aspp-asia-pacific/applications (make sure you read the FAQ on that page)&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- TEASER_END --&gt;

&lt;h3&gt;Background&lt;/h3&gt;

&lt;p&gt;Two-and-a-bit years ago, Tiziano Zito asked me if I could join the faculty at the 2015 ASPP school in Munich (then in its 8th iteration). It turned out to be a fantastic teaching experience, and, more importantly, it was a fantastic experience for the students. Students selected for the school fit a certain profile, neither novice nor advanced. As such, you can be sure that if you participate in the school, you will learn a great deal. We teach tools that will immediately improve your scientific practice. I decided that I wanted to replicate the school in Australia. Now it is finally here!&lt;/p&gt;
&lt;h3&gt;Course outline&lt;/h3&gt;

&lt;p&gt;Scientists spend increasingly more time writing, maintaining, and debugging software. While techniques for doing this efficiently have evolved, only few scientists have been trained to use them. As a result, instead of doing their research, they spend far too much time writing deficient code and reinventing the wheel. In this course we will present a selection of advanced programming techniques and best practices that are standard in industry, but especially tailored to the needs of a programming scientist. Lectures are devised to be interactive and to give the students enough time to acquire direct hands-on experience with the materials. Students will work in pairs throughout the school and will team up to practice the newly learned skills in a real programming project — an entertaining computer game.&lt;/p&gt;
&lt;p&gt;We use the Python programming language for the entire course. Python works as a simple programming language for beginners, but more importantly, it also works great in scientific simulations and data analysis. We show how clean language design, ease of extensibility, and the great wealth of open source libraries for scientific computing and data visualization are driving Python to becoming a standard tool for scientists.&lt;/p&gt;
&lt;h3&gt;Who is eligible?&lt;/h3&gt;

&lt;p&gt;This school is targeted at Master/PhD students and postdocs from all areas of science. Competence in Python or in another language such as Java, C/C++, MATLAB, or Mathematica is absolutely required. Basic knowledge of Python and of a version control system such as git, subversion, mercurial, or bazaar is assumed. Participants without any prior experience with Python and/or git should work through the proposed introductory material &lt;strong&gt;before&lt;/strong&gt; the course.&lt;/p&gt;
&lt;p&gt;We have strived to get a pool of students that is international and gender-balanced, and have succeeded, with gender parity in the last four schools.&lt;/p&gt;
&lt;h3&gt;More questions&lt;/h3&gt;

&lt;p&gt;If you have any questions, contact &lt;a href="mailto:aspp@melbournebioinformatics.org.au"&gt;aspp@melbournebioinformatics.org.au&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please circulate this announcement widely!&lt;/p&gt;
&lt;p&gt;Thanks,&lt;/p&gt;
&lt;p&gt;Juan.&lt;/p&gt;&lt;/div&gt;</description><category>conference</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>Scientific Computing</category><category>Workshops</category><guid>https://ilovesymposia.com/2017/10/05/summer-school-announcement-aspp-asia-pacific-2018/</guid><pubDate>Thu, 05 Oct 2017 03:21:30 GMT</pubDate></item><item><title>Prettier LowLevelCallables with Numba JIT and decorators</title><link>https://ilovesymposia.com/2017/03/15/prettier-lowlevelcallables-with-numba-jit-and-decorators/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;In my &lt;a href="https://ilovesymposia.com/2017/03/12/scipys-new-lowlevelcallable-is-a-game-changer/"&gt;recent post&lt;/a&gt;, I extolled the virtues of SciPy 0.19's &lt;code&gt;LowLevelCallable&lt;/code&gt;. I did lament, however, that for &lt;code&gt;generic_filter&lt;/code&gt;, the &lt;code&gt;LowLevelCallable&lt;/code&gt; interface is a good deal uglier than the standard function interface. In the latter, you merely need to provide a function that takes the values within a pixel neighbourhood, and outputs a single value — an arbitrary function of the input values. That is a Wholesome and Good filter function, the way God intended.&lt;/p&gt;
&lt;p&gt;In contrast, a LowLevelCallable takes the following signature:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;intptr_t&lt;/span&gt; &lt;span class="n"&gt;filter_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
             &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;That’s not very Pythonic at all. In fact, it’s positively Conic (TM). For those that don’t know,
&lt;a href="https://www.quora.com/Why-do-people-think-pointers-are-evil"&gt;pointers are evil&lt;/a&gt;,
so let’s aim to avoid their use.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;“But Juan!”, you are no doubt exclaiming. “Juan! Didn’t you &lt;em&gt;just&lt;/em&gt; tell us how to use pointers in Numba cfuncs, and tell us how great it was because it was so fast?”&lt;/p&gt;
&lt;p&gt;Indeed I did. But it left a bad taste in my mouth. Although I felt that the tradeoff was worth it for such a phenomenal speed boost (300x!), I was unsatisfied. So I started immediately to look for a tidier solution. One that would let me write proper filter functions while still taking advantage of LowLevelCallables.&lt;/p&gt;
&lt;p&gt;It turns out Numba cfuncs &lt;em&gt;can call Numba jitted functions&lt;/em&gt;, so, with a little bit of decorator magic, it’s now ludicrously easy to write performant callables for SciPy using just pure Python. (If you don’t know what Numba JIT is, &lt;a href="https://ilovesymposia.com/2016/12/20/numba-in-the-real-world/"&gt;read my earlier post&lt;/a&gt;.) As in the last post, let’s look at &lt;code&gt;grey_erosion&lt;/code&gt; as a baseline benchmark:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;footprint&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;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="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;1&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="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="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndimage&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grey_erosion&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;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;160&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;Now, we write a decorator that uses Numba jit &lt;em&gt;and&lt;/em&gt; Numba cfunc to make a LowLevelCallable suitable for passing directly into &lt;code&gt;generic_filter&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numba&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cfunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carray&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;intc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CPointer&lt;/span&gt;&lt;span class="p"&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;intp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;voidptr&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LowLevelCallable&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;jit_filter_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_function&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;jitted_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numba&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nopython&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@cfunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CPointer&lt;/span&gt;&lt;span class="p"&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;intp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CPointer&lt;/span&gt;&lt;span class="p"&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;voidptr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values_ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len_values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;carray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values_ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len_values&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;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&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="n"&gt;jitted_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LowLevelCallable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;If you haven’t seen decorators before, read &lt;a href="https://realpython.com/blog/python/primer-on-python-decorators/"&gt;this primer&lt;/a&gt; from Real Python. To summarise, we’ve written a function that takes as input a Python function, and outputs a LowLevelCallable. Here’s how to use it:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@jit_filter_function&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&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;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inf&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&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;v&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;p&gt;As you can see, the &lt;code&gt;fmin&lt;/code&gt; function definition looks just like a normal Python function. All the magic happens when we attach our &lt;code&gt;@jit_filter_function&lt;/code&gt; decorator to the top of the function. Let’s see it in action:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generic_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;fmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;92.9&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;Wow! &lt;code&gt;numba.jit&lt;/code&gt; is actually over 70% faster than &lt;code&gt;grey_erosion&lt;/code&gt; or the plain &lt;code&gt;cfunc&lt;/code&gt; approach!&lt;/p&gt;
&lt;p&gt;In case you want to use this, I’ve made a package &lt;a href="https://pypi.python.org/pypi/llc"&gt;available on PyPI&lt;/a&gt;, so you can actually pip install it right now with &lt;code&gt;pip install llc&lt;/code&gt; (for low-level callable), and then:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;llc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jit_filter_function&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;The source code is &lt;a href="https://github.com/jni/llc-tools"&gt;on GitHub&lt;/a&gt;. Currently it only covers &lt;code&gt;ndi.generic_filter&lt;/code&gt;'s signature, and only with Numba, but I hope to gradually expand it to cover all the functions that take &lt;code&gt;LowLevelCallables&lt;/code&gt; in SciPy, as well as support Cython. Pull requests are welcome!&lt;/p&gt;&lt;/div&gt;</description><category>Numba</category><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>SciPy</category><guid>https://ilovesymposia.com/2017/03/15/prettier-lowlevelcallables-with-numba-jit-and-decorators/</guid><pubDate>Tue, 14 Mar 2017 13:14:49 GMT</pubDate></item><item><title>SciPy's new LowLevelCallable is a game-changer</title><link>https://ilovesymposia.com/2017/03/12/scipys-new-lowlevelcallable-is-a-game-changer/</link><dc:creator>Juan Nunez-Iglesias</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;... and combines rather well with that other game-changing library I like, &lt;a href="https://ilovesymposia.com/2016/12/20/numba-in-the-real-world/"&gt;Numba&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've &lt;a href="https://ilovesymposia.com/2015/12/10/the-cost-of-a-python-function-call/"&gt;lamented before&lt;/a&gt; that function calls are expensive in Python, and that this severely hampers many functions that &lt;em&gt;should&lt;/em&gt; be insanely useful, such as SciPy's &lt;a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.generic_filter.html#scipy.ndimage.generic_filter"&gt;&lt;code&gt;ndimage.generic_filter&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;

&lt;p&gt;To illustrate this, let's look at image &lt;em&gt;erosion&lt;/em&gt;, which is the replacement of each pixel in an image by the minimum of its neighbourhood. &lt;code&gt;ndimage&lt;/code&gt; has a fast C implementation, which serves as a perfect benchmark against the generic version, using a generic filter with &lt;code&gt;min&lt;/code&gt; as the operator. Let's start with a 2048 x 2048 random image:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;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;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;random&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;and a neighbourhood “footprint” that picks out the pixels to the left and right, and above and below, the centre pixel:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;footprint&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;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="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;1&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="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="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;Now, we measure the speed of &lt;code&gt;grey_erosion&lt;/code&gt; and &lt;code&gt;generic_filter&lt;/code&gt;. Spoiler alert: it’s not pretty.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndimage&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grey_erosion&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;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;118&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generic_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;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;27&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;As you can see, with Python functions, &lt;code&gt;generic_filter&lt;/code&gt; is unusable for anything but the tiniest of images.&lt;/p&gt;
&lt;p&gt;A few months ago, I was
&lt;a href="https://groups.google.com/a/continuum.io/d/msg/numba-users/HMg_65R8KZE/RnysYokGAwAJ"&gt;trying&lt;/a&gt;
to get around this by using Numba-compiled functions, but the way to feed C
functions to SciPy was different depending on which part of the library you
were using. &lt;code&gt;scipy.integrate&lt;/code&gt; used ctypes, while &lt;code&gt;scipy.ndimage&lt;/code&gt; used &lt;code&gt;PyCObjects&lt;/code&gt; or
&lt;code&gt;PyCapsules&lt;/code&gt;, depending on your Python version, and Numba only supported the
former method at the time. (Plus, this topic starts to stretch my understanding
of low-level Python, so I felt there wasn’t much I could do about it.)&lt;/p&gt;
&lt;p&gt;Enter &lt;a href="https://github.com/scipy/scipy/pull/6509"&gt;this pull request&lt;/a&gt; to SciPy
from Pauli Virtanen, which is live in the most recent SciPy version, 0.19. It
unifies all C-function interfaces within SciPy, and Numba already supports this
format. It takes a bit of gymnastics, but it works! It really works!&lt;/p&gt;
&lt;p&gt;(By the way, the release is full of little gold nuggets. If you use SciPy at
all, the release notes are well worth a read.)&lt;/p&gt;
&lt;p&gt;First, we need to define a C function of the appropriate signature. Now, you
might think this is the same as the Python signature, taking in an array of
values and returning a single value, but that would be too easy! Instead, we
have to go back to some C-style programming with pointers and array sizes. From
the &lt;code&gt;generic_filter&lt;/code&gt; documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This function also accepts low-level callback functions with one of the
following signatures and wrapped in scipy.LowLevelCallable:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c
int callback(double *buffer, npy_intp filter_size, 
             double *return_value, void *user_data)
int callback(double *buffer, intptr_t filter_size, 
             double *return_value, void *user_data)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The calling function iterates over the elements of the input and output
arrays, calling the callback function at each element. The elements within
the footprint of the filter at the current element are passed through the
buffer parameter, and the number of elements within the footprint through
filter_size. The calculated value is returned in return_value. user_data is
the data pointer provided to scipy.LowLevelCallable as-is.&lt;/p&gt;
&lt;p&gt;The callback function must return an integer error status that is zero if
something went wrong and one otherwise. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Let’s leave aside that crazy reversal of Unix convention of the past 50 years
in the last paragraph, except to note that our function must return 1 or it
will be killed.)&lt;/p&gt;
&lt;p&gt;So, we need a Numba cfunc that takes in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a double pointer pointing to the values within the footprint,&lt;/li&gt;
&lt;li&gt;a pointer-sized integer that specifies the number of values in the footprint,&lt;/li&gt;
&lt;li&gt;a double pointer for the result, and&lt;/li&gt;
&lt;li&gt;a void pointer, which could point to additional parameters, but which we can ignore for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Numba type names are listed in &lt;a href="http://numba.pydata.org/numba-doc/dev/reference/types.html#numba-types"&gt;this page&lt;/a&gt;. Unfortunately, at the time of
writing, there’s no mention of how to make pointers there, but finding such a
reference &lt;a href="http://numba.pydata.org/numba-doc/dev/user/cfunc.html#signature-specification"&gt;was not too hard&lt;/a&gt;. (Incidentally, it would make a good contribution to
Numba’s documentation to add CPointer to the Numba types page.)&lt;/p&gt;
&lt;p&gt;So, armed with all that documentation, and after much trial and error, I was
finally ready to write that C callable:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cfunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carray&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;intc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intp&lt;/span&gt;&lt;span class="p"&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;voidptr&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numba.types&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CPointer&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@cfunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CPointer&lt;/span&gt;&lt;span class="p"&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;intp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;CPointer&lt;/span&gt;&lt;span class="p"&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;voidptr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;nbmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values_ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len_values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;carray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values_ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len_values&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;float64&lt;/span&gt;&lt;span class="p"&gt;)&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="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="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inf&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;result&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="n"&gt;result&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="n"&gt;v&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;The only other tricky bits I had to watch out for while writing that function were as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;remembering that there’s two ways to de-reference a pointer in C: &lt;code&gt;*ptr&lt;/code&gt;,
   which is not valid Python and thus not valid Numba, and &lt;code&gt;ptr[0]&lt;/code&gt;. So, to place
   the result at the given double pointer, we use the latter syntax. (If you
   prefer to use Cython, the same rule applies.)&lt;/li&gt;
&lt;li&gt;Creating an array out of the &lt;code&gt;values_ptr&lt;/code&gt; and &lt;code&gt;len_values&lt;/code&gt; variables, as shown
   here. That’s what enables the &lt;code&gt;for v in values&lt;/code&gt; Python-style access to the
   array.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, so now what you’ve been waiting for. How did we do? First, to recap, the original benchmarks:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ndimage&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grey_erosion&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;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;118&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generic_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;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;27&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;And now, with our new Numba cfunc:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt; &lt;span class="n"&gt;ndi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generic_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;LowLevelCallable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nbmin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ctypes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;footprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;of&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;113&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;That's right: it's even marginally &lt;em&gt;faster&lt;/em&gt; than the pure C version! I almost cried when I ran that.&lt;/p&gt;


&lt;hr&gt;

&lt;p&gt;Higher-order functions, ie functions that take other functions as input, enable powerful, concise, elegant &lt;a href="https://ilovesymposia.com/2014/06/24/a-clever-use-of-scipys-ndimage-generic_filter-for-n-dimensional-image-processing/"&gt;expressions&lt;/a&gt; of various algorithms. Unfortunately, these have been hampered in Python for large-scale data processing because of Python's function call overhead. SciPy's latest update goes a long way towards redressing this.&lt;/p&gt;&lt;/div&gt;</description><category>Numba</category><category>open-source</category><category>Planet SciPy</category><category>programming</category><category>Python</category><category>science</category><category>SciPy</category><guid>https://ilovesymposia.com/2017/03/12/scipys-new-lowlevelcallable-is-a-game-changer/</guid><pubDate>Sun, 12 Mar 2017 03:41:41 GMT</pubDate></item></channel></rss>