Cris’ Image Analysis Blog - announcementshttps://www.crisluengo.net/2022-12-09T00:00:00-07:00theory, methods, algorithms, applicationsDIPlib 3.4.0 released2022-12-09T00:00:00-07:002022-12-09T00:00:00-07:00Cris Luengotag:www.crisluengo.net,2022-12-09:/archives/1139<p>Yesterday we released DIPlib version 3.4.0. The <a href="https://diplib.org/changelogs/diplib_3.4.0.html">change log</a>
is quite extensive. The improved median filter I discussed in <a href="/archives/1138" title="Median filtering">my previous blog post</a>
is in this release, as well as a much faster built-in FFT implementation (used when not linking against FFTW),
a bunch of new functionality including …</p><p>Yesterday we released DIPlib version 3.4.0. The <a href="https://diplib.org/changelogs/diplib_3.4.0.html">change log</a>
is quite extensive. The improved median filter I discussed in <a href="/archives/1138" title="Median filtering">my previous blog post</a>
is in this release, as well as a much faster built-in FFT implementation (used when not linking against FFTW),
a bunch of new functionality including various iterative
<a href="https://github.com/DIPlib/diplib/blob/master/examples/python/deconvolution.ipynb">deconvolution algorithms</a>,
and some bug fixes.</p>
<h2>New function forms in Python bindings</h2>
<p>DIPib has always had <a href="https://diplib.org/diplib-docs/design.html#design_function_signatures">two signatures for each function</a>,
one where the output image is passed as an input argument to be modified, one that returns the output image.
For example, the <code>dip::Gauss</code> function can be called in these two ways:</p>
<div class="highlight"><pre><span></span><code><span class="n">dip</span><span class="o">::</span><span class="n">Image</span><span class="w"> </span><span class="n">out</span><span class="p">;</span><span class="w"></span>
<span class="n">dip</span><span class="o">::</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span><span class="w"> </span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="mi">3</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="p">},</span><span class="w"> </span><span class="s">"FIR"</span><span class="p">);</span><span class="w"></span>
<span class="n">dip</span><span class="o">::</span><span class="n">Image</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="mi">3</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="p">},</span><span class="w"> </span><span class="s">"FIR"</span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<p>(I’m purposefully specifying more parameters here than necessary, bear with me.)</p>
<p>When I first created the Python bindings, PyDIP, I only created bindings for the second form, with the output
image as return value. It is the more user-friendly form, and I might have been subconsciously imitating the
MATLAB syntax of DIPimage. In this latest release, we’re adding bindings for the other form as well.</p>
<p>In Python it is not possible to have multiple signatures for one function. We use Pybind11 to create the
Python bindings for DIPlib, which does allow multiple overloaded functions to be called from Python. It has
an overload resolution algorithm built-in, where it attempts to convert input arguments to the types needed
by the first overload, if it fails it tries the second overload, etc. But Python is not as strongly typed as C++,
so it can be difficult to avoid certain type conversions from happening, making the two overloads difficult
to distinguish for some functions. To avoid ambiguity, we made the output image argument a keyword-only argument.
All arguments that come after must be keyword-only as well, as per Python syntax.
So, whenever there is an <code>out=xxx</code> argument specified, we mean the one function form, and whenever that argument
is not specified, we mean the other function form. This has the added benefit of making the output image argument
clearly recognizable.</p>
<p>For example, where previously you could call <code>dip.Gauss</code> as</p>
<div class="highlight"><pre><span></span><code><span class="n">out</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>or</p>
<div class="highlight"><pre><span></span><code><span class="n">out</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>you can now also call it as</p>
<div class="highlight"><pre><span></span><code><span class="n">out</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Image</span><span class="p">()</span>
<span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">out</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>(I mean to say <code>sigmas</code> and <code>method</code> must be given as keyword arguments in this new form).</p>
<p>Note, in that last call, that we didn’t need to allocate space for <code>out</code>, we didn’t need to worry about how large
the output image would be, or what data type it would have. The DIPlib function will reallocate the output image
to match its needs. </p>
<h2>Why bother?</h2>
<p>The reason that I wanted to add this alternate, less user-friendly form of each function, is because of the
possibilities it opens up. The obvious advantage is that it allows re-using already allocated images, reducing
memory requirements, and possibly speed up some computations by working in-place. For example,</p>
<div class="highlight"><pre><span></span><code><span class="n">dip</span><span class="o">.</span><span class="n">Abs</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">img</span><span class="p">)</span>
</code></pre></div>
<p>works faster than</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Abs</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
</code></pre></div>
<p>But the more important advantage is being able to determine the data type of the output. For this to work we
need <a href="https://diplib.org/diplib-docs/dip-Image.html#protect">the “protect” flag</a> (this works the same as in
the DIPlib C++ library of course). The “protect” flag protects an image from being reallocated. That is, if an
image is protected, its data segment cannot be freed or changed. This makes the image’s sizes and data type
fixed. So one could create, for example, complex-valued image, and use that as the output:</p>
<div class="highlight"><pre><span></span><code><span class="n">out</span> <span class="o">=</span> <span class="n">img</span><span class="o">.</span><span class="n">Similar</span><span class="p">(</span><span class="s2">"SCOMPLEX"</span><span class="p">)</span>
<span class="n">out</span><span class="o">.</span><span class="n">Protect</span><span class="p">()</span>
<span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">out</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>The image being protected, the <code>dip.Gauss</code> function must use it as-is for its output, and so will produce,
in this case, a complex-valued result even if it would otherwise have produced a real-valued floating-point
result. Note that if the protected image were to have the wrong sizes or number of tensor elements, an
exception would be raised.</p>
<p>To make this process simpler (i.e. letting the filter function do the allocation), DIPlib has another trick:
it is possible to protect a raw image (an image without a data segment). For such an image it is still
possible to change its sizes and number of tensor elements, and then allocate its data segment. But all
DIPlib functions will see the “protect” flag and not change the data type.
This allows the following code to work as expected:</p>
<div class="highlight"><pre><span></span><code><span class="n">out</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Image</span><span class="p">()</span>
<span class="n">out</span><span class="o">.</span><span class="n">SetDataType</span><span class="p">(</span><span class="s2">"SCOMPLEX"</span><span class="p">)</span>
<span class="n">out</span><span class="o">.</span><span class="n">Protect</span><span class="p">()</span>
<span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">out</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<h2>In-place processing</h2>
<p>The following code might not work in place:</p>
<div class="highlight"><pre><span></span><code><span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">img</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>If <code>img</code> is of an integer type, the output will be adjusted to be a floating-point type, and so will still create
a new output data segment. To force a function to work in place, protect the image:</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span><span class="o">.</span><span class="n">Protect</span><span class="p">()</span>
<span class="n">dip</span><span class="o">.</span><span class="n">Gauss</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">out</span><span class="o">=</span><span class="n">img</span><span class="p">,</span> <span class="n">sigmas</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">"FIR"</span><span class="p">)</span>
</code></pre></div>
<p>Some functions cannot work in place, they will always create an intermediate image to do the computation, maybe
by copying the input image so that it can be used as a separate output image. Some of the functions that cannot
work in place will raise an exception if the input and output images are the same. There will always be things
that could be improved!</p>
<p>Let us know what you think of this new functionality!</p>DIPlib 3.3.0 released2022-05-21T00:00:00-06:002022-05-21T00:00:00-06:00Cris Luengotag:www.crisluengo.net,2022-05-21:/archives/1137<p>Today saw another DIPlib release, version 3.3.0.
As usual, there is some new functionality, some improvements to existing functionality,
and some bug fixes, see the <a href="https://diplib.org/changelogs/diplib_3.3.0.html">change log</a>
for details. An increasing number of people is using the Python bindings, and I’ve been
using them extensively at work …</p><p>Today saw another DIPlib release, version 3.3.0.
As usual, there is some new functionality, some improvements to existing functionality,
and some bug fixes, see the <a href="https://diplib.org/changelogs/diplib_3.3.0.html">change log</a>
for details. An increasing number of people is using the Python bindings, and I’ve been
using them extensively at work now as well. And so a lot of the changes in this release
are usability improvements to these bindings.</p>
<p>For example, it is now possible to choose (in Python) which dimension order to use.
By default the order is the same as in the C++ library (and the order that makes intuitively
most sense to an old-timer like me): x, y, z, … NumPy arrays are typically interpreted
as the last dimension being horizontal (x), so a 3D image would be indexed as (z, y, x).</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Image</span><span class="p">((</span><span class="mi">30</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
<span class="o">>>></span> <span class="n">img</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">img</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span>
<span class="o">>>></span> <span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="mf">0.0</span>
<span class="o">>>></span> <span class="n">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="mf">100.0</span>
</code></pre></div>
<p>To me this is awkward, but people that first learned image processing with Python are used
to that order. So we introduced <code>dip.ReverseDimensions()</code>, which reverses the order of the
dimensions of images in PyDIP (in the Python binds to DIPlib, not DIPlib itself). This
function is most useful when combining DIPlib with other imaging libraries such as
skimage, which uses NumPy arrays to store images, and matches the array’s dimension order
when specifying, for example, filter sizes (that is, filters are specified as z, y, x also).</p>
<div class="highlight"><pre><span></span><code><span class="o">>>></span> <span class="n">dip</span><span class="o">.</span><span class="n">ReverseDimensions</span><span class="p">()</span>
<span class="o">>>></span> <span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Image</span><span class="p">((</span><span class="mi">30</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
<span class="o">>>></span> <span class="n">img</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">img</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span>
<span class="o">>>></span> <span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">asarray</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="mf">100.0</span>
<span class="o">>>></span> <span class="n">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="mf">0.0</span>
</code></pre></div>
<p>It is important to call <code>dip.ReverseDimensions()</code> only at the beginning of a program,
running it half-way could cause a lot of confusion. Also, you cannot use this within
a function: it always affects the whole program, and cannot be reversed.</p>
<div class="admonition blue">
<p>Because I like to find fault in OpenCV, let me point out here that OpenCV also uses
NumPy arrays to store images, but the OpenCV functions take coordinates and sizes in the
x, y order, meaning that it is internally inconsistent!</p>
</div>
<p>The biggest change this release is the introduction of the <code>@</code> operator for matrix
multiplication of tensor images, and the change of the <code>*</code> operator, which now always
does element-wise multiplication.</p>
<p>When I created the Python bindings to DIPlib, I implemented the <code>*</code> operator for <code>dip.Image</code>
objects to do the same thing as the <code>*</code> operator does for <code>dip::Image</code> objects in C++:
matrix multiplication of tensor images. C++ does not have a separate operator for
element-wise multiplication and matrix multiplication, as Python does. So it makes sense,
in C++, to pick matrix multiplication as the meaning of the <code>*</code> operator. But in Python
this choice was awkward. One needs to go through hoops to do an element-wise multiplication
(granted, it is not needed all that often), and the meaning of the operators do not match
what is expected by the user.</p>
<p>And so we decided to introduce this change, even though it might break existing code.
Because PyDIP is still rather rough, <a href="https://github.com/DIPlib/diplib/issues/105#issuecomment-1055859935">it would be very limiting to never introduce
breaking changes at this point</a>.</p>
<p>Do note that both operators do exactly the same thing if one of the operands is a scalar
image or a scalar constant; this change affects only code that uses matrix multiplications.</p>
<p>Here is an example of using the element-wise multiplication: min-max scaling of each channel
of the image individually:</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">-=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Minimum</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="n">img</span> <span class="o">*=</span> <span class="mi">255</span> <span class="o">/</span> <span class="n">dip</span><span class="o">.</span><span class="n">Maximum</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
</code></pre></div>
<p>Here is an example of using the matrix multiplication: converting the RGB image to gray scale
(obviously one would normally use <code>dip.ColorSpaceManager</code> for this purpose):</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Transpose</span><span class="p">(</span><span class="n">img</span><span class="p">)</span> <span class="o">@</span> <span class="p">[</span><span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.6</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">]</span>
</code></pre></div>
<p>Yes, the syntax here is still a bit awkward, a list of values is always interpreted as a
column vector, even if it looks more like a row vector.
And we cannot use <code>img @= [...]</code> because the image needs to be transposed first.</p>
<p>For more complex examples using tensor image arithmetics, see <a href="https://diplib.org/diplib-docs/why_tensors.html">the “Why tensors?” page in
the documentation</a>, or a blog post I wrote
last year about <a href="/archives/1132">“The Hessian and the Structure Tensor”</a></p>
<p>If you have suggestions for improving the syntax in PyDIP (or any other aspect!) please
let us know by <a href="https://github.com/DIPlib/diplib/issues">creating an issue on GitHub</a>,
or through the LinkedIn or Twitter threads linked below. Thank you!</p>DIPlib 3.2.0 released2022-02-12T00:00:00-07:002022-02-12T00:00:00-07:00Cris Luengotag:www.crisluengo.net,2022-02-12:/archives/1136<p>We made a new release for DIPlib this week, version 3.2.0
(see the <a href="https://diplib.org/changelogs/diplib_3.2.0.html">change log</a>).
There is some added functionality to the C++ library, such as the ability to render text
in an image, but otherwise it’s mostly smaller usability tweaks and bug fixes.
The biggest changes …</p><p>We made a new release for DIPlib this week, version 3.2.0
(see the <a href="https://diplib.org/changelogs/diplib_3.2.0.html">change log</a>).
There is some added functionality to the C++ library, such as the ability to render text
in an image, but otherwise it’s mostly smaller usability tweaks and bug fixes.
The biggest changes were to the Python bindings, PyDIP.
Of these, the changes to <code>dip.Histogram()</code> and <code>dip.LookupTable()</code> are the most important
ones, because they break backwards compatibility. We aim at keeping old code running with
newer versions of DIPlib, but this was a necessary change.</p>
<p>In previous versions, <code>dip.Histogram()</code> and <code>dip.LookupTable()</code> were functions that used
the <a href="https://diplib.org/diplib-docs/dip-Histogram.html"><code>dip::Histogram</code></a> and the
<a href="https://diplib.org/diplib-docs/dip-LookupTable.html"><code>dip::LookupTable</code></a> classes from
the C++ library. This seemed convenient at the time, but turned out to be very limiting.
The C++ classes are now mapped to Python, opening up a whole host of functionality that
wasn’t available from Python previously. An additional benefit is that the documentation
for the C++ library can now be used by Python users to understand how to use the library
functionality from within Python.</p>
<p>What this means for old code is that <code>dip.Histogram()</code> no longer returns a tuple with
an array of bin values and an array of bin centers, but returns a <code>dip.Histogram</code> object.
Likewise, <code>dip.LookupTable()</code> no longer is a function that applies a lookup table to an
image, returning an image, but is a constructor that returns a <code>dip.LookupTable</code> object.</p>
<p>To keep old code working there are two options:</p>
<ol>
<li>
<p>After importing the <code>diplib</code> package, assign <code>dip.Histogram = dip.Histogram_old</code> and/or
<code>dip.LookupTable = dip.LookupTable_old</code>, to recover the behavior of previous versions.</p>
</li>
<li>
<p>Rewrite your code to use the new classes. Below I’ll demonstrate how these classes can
be used, and what the advantages are.</p>
</li>
</ol>
<h2><code>dip.Histogram</code></h2>
<p>The <code>dip.Histogram</code> class has constructors similar to
<a href="https://diplib.org/diplib-docs/dip-Histogram.html#typeless-methods">those in the C++ library</a>,
using an object of class <code>dip.Histogram.Configuration</code>, or an array of them for
multi-dimensional histograms, to define the histogram parameters. In C++, we can construct
a histogram as follows:</p>
<div class="highlight"><pre><span></span><code><span class="n">dip</span><span class="o">::</span><span class="n">Histogram</span><span class="w"> </span><span class="nf">hist1</span><span class="p">(</span><span class="w"> </span><span class="n">img</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">255.0</span><span class="p">,</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="n">dip</span><span class="o">::</span><span class="n">Histogram</span><span class="w"> </span><span class="nf">hist2</span><span class="p">(</span><span class="w"> </span><span class="n">img</span><span class="p">,</span><span class="w"> </span><span class="p">{},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">255.0</span><span class="p">,</span><span class="w"> </span><span class="mf">1.7</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<p>resulting in a histogram <code>hist1</code> with bounds [0.0, 255.0] and 100 bins, and a histogram
<code>hist2</code> with the same bounds, and a bin width of 1.7 (it is the distinction between an
integer and a float that decides how the third number is interpreted). In Python the
equivalent constructors could be called as follows:</p>
<div class="highlight"><pre><span></span><code><span class="n">hist1</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="o">.</span><span class="n">Configuration</span><span class="p">(</span><span class="n">lowerBound</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">upperBound</span><span class="o">=</span><span class="mi">255</span><span class="p">,</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">))</span>
<span class="n">hist2</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="o">.</span><span class="n">Configuration</span><span class="p">(</span><span class="n">lowerBound</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">upperBound</span><span class="o">=</span><span class="mi">255</span><span class="p">,</span> <span class="n">binSize</span><span class="o">=</span><span class="mf">1.7</span><span class="p">))</span>
</code></pre></div>
<p>This is obviously much more verbose. This is why we added a constructor that takes the
parameters as individual values:</p>
<div class="highlight"><pre><span></span><code><span class="n">hist1</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">bounds</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</code></pre></div>
<p>This constructor cannot be used when one wants to set the bin size explicitly. Also when
other configuration options are needed, an explicit <code>dip.Histogram.Configuration</code> object
should be constructed. On the other hand, the <code>dip.Histogram.Configuration</code> object is
useful when constructing multiple histograms that must be comparable.</p>
<p>Old code that looked like this:</p>
<div class="highlight"><pre><span></span><code><span class="n">hist</span><span class="p">,</span> <span class="n">bins</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span> <span class="c1"># old syntax</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">bins</span><span class="p">,</span> <span class="n">hist</span><span class="p">)</span> <span class="c1"># import matplotlib.pyplot as plt</span>
</code></pre></div>
<p>can now be written as:</p>
<div class="highlight"><pre><span></span><code><span class="n">hist</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">hist</span><span class="o">.</span><span class="n">BinCenters</span><span class="p">(),</span> <span class="n">hist</span><span class="o">.</span><span class="n">GetImage</span><span class="p">())</span>
</code></pre></div>
<p>or simply as:</p>
<div class="highlight"><pre><span></span><code><span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span><span class="o">.</span><span class="n">Show</span><span class="p">()</span>
</code></pre></div>
<p>Besides methods to inspect the histogram, there are methods like <code>Cumulative()</code>, and
<code>Smooth()</code> that transform the histogram, or <code>GetMarginal()</code> that returns the marginal
histogram for any one of its dimensions. There is also a long list of free functions
to compute statistics from the histogram, to compute thresholds, and to do clustering.</p>
<p>For example, this is the way to apply k-means clustering to the intensities of an image:</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">ImageRead</span><span class="p">(</span><span class="s1">'DIP.tif'</span><span class="p">)</span> <span class="c1"># from here: https://github.com/DIPlib/diplib/blob/master/examples/DIP.tif</span>
<span class="n">img</span><span class="o">.</span><span class="n">Show</span><span class="p">()</span>
<span class="n">hist</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">nBins</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
<span class="n">hist</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">KMeansClustering</span><span class="p">(</span><span class="n">hist</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># 3 clusters</span>
<span class="n">lab</span> <span class="o">=</span> <span class="n">hist</span><span class="o">.</span><span class="n">ReverseLookup</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="n">lab</span><span class="o">.</span><span class="n">Show</span><span class="p">(</span><span class="s1">'labels'</span><span class="p">)</span>
</code></pre></div>
<p>(note that the function <code>dip.KMeansClustering</code>, when applied to an image, clusters
the image spatially, not its intensities as we did here.)</p>
<p class="centering"><img alt="Input to code above" src="/images/diplib_3_2_input.png"> <img alt="Output of code above" src="/images/diplib_3_2_kmeans.png"></p>
<p>There are also two functions, <code>dip.EqualizationLookupTable()</code> and <code>dip.MatchingLookupTable()</code>,
that return a <code>dip.LookupTable</code> object from respectively one and two input histograms.</p>
<h2><code>dip.LookupTable</code></h2>
<p>The <code>dip.LookupTable</code> class is much simpler than the <code>dip.Histogram</code> class. There is
one constructor, which takes an image or NumPy array as input, and optionally also
corresponding indices. The image must be 1D, but can have multiple samples per pixel.
There are some methods that change the lookup table’s behavior with respect to out of
bounds lookups, but the most important method is <code>Apply</code>.</p>
<p>Here we use the <code>dip.LookupTable</code> class to paint each label in the <code>lab</code> image we created
above with the average color of each </p>
<div class="highlight"><pre><span></span><code><span class="n">msr</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">MeasurementTool</span><span class="o">.</span><span class="n">Measure</span><span class="p">(</span><span class="n">lab</span><span class="p">,</span> <span class="n">img</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Mean'</span><span class="p">])</span>
<span class="n">colors</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Image</span><span class="p">(</span><span class="n">msr</span><span class="p">[</span><span class="s1">'Mean'</span><span class="p">],</span> <span class="n">tensor_axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">colors</span><span class="o">.</span><span class="n">SetColorSpace</span><span class="p">(</span><span class="s1">'srgb'</span><span class="p">)</span>
<span class="n">lut</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">LookupTable</span><span class="p">(</span><span class="n">colors</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span> <span class="c1"># indices in lab start at 1</span>
<span class="n">lut</span><span class="o">.</span><span class="n">Convert</span><span class="p">(</span><span class="s1">'UINT8'</span><span class="p">)</span>
<span class="n">out</span> <span class="o">=</span> <span class="n">lut</span><span class="o">.</span><span class="n">Apply</span><span class="p">(</span><span class="n">lab</span><span class="p">)</span>
<span class="n">out</span><span class="o">.</span><span class="n">Show</span><span class="p">()</span>
</code></pre></div>
<p class="centering"><img alt="Output of code above" src="/images/diplib_3_2_out.png"></p>
<p>Although the above could have been done just as easily with the <code>dip.LookupTable()</code> function
that existed previously, it is convenient to have the lookup table be an object because it
can be applied repeatedly, to many images. Also, we now can make functions such as
<code>dip.EqualizationLookupTable</code> available in Python. This function can be used to create a
lookup table that will equalize (flatten) the histogram of an image. This would be interesting
over simply applying the function <code>dip.HistogramEqualization()</code> because it makes it possible
to examine the mapping applied, and apply the same mapping to other images. For example,</p>
<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">ImageRead</span><span class="p">(</span><span class="s1">'examples/trui'</span><span class="p">)</span> <span class="c1"># from here: https://github.com/DIPlib/diplib/blob/master/examples/trui.ics</span>
<span class="n">hist</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">Histogram</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="n">lut</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">EqualizationLookupTable</span><span class="p">(</span><span class="n">hist</span><span class="p">)</span>
<span class="n">imgeq</span> <span class="o">=</span> <span class="n">lut</span><span class="o">.</span><span class="n">Apply</span><span class="p">(</span><span class="n">img1</span><span class="p">)</span> <span class="c1"># histogram equalized</span>
<span class="n">lut</span><span class="o">.</span><span class="n">Apply</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">256</span><span class="p">))</span><span class="o">.</span><span class="n">Show</span><span class="p">()</span>
</code></pre></div>
<p class="centering"><img alt="Mapping visualized" src="/images/diplib_3_2_mapping.png"></p>
<div class="highlight"><pre><span></span><code><span class="n">img2</span> <span class="o">=</span> <span class="n">dip</span><span class="o">.</span><span class="n">ImageRead</span><span class="p">(</span><span class="s1">'examples/cermet'</span><span class="p">)</span> <span class="c1"># from here: https://github.com/DIPlib/diplib/blob/master/examples/cermet.ics</span>
<span class="n">img2eq</span> <span class="o">=</span> <span class="n">lut</span><span class="o">.</span><span class="n">Apply</span><span class="p">(</span><span class="n">img2</span><span class="p">)</span> <span class="c1"># histogram mapped in the same way, not equalized</span>
</code></pre></div>DIPlib 3.0.0 released2021-02-23T00:00:00-07:002021-02-23T00:00:00-07:00Cris Luengotag:www.crisluengo.net,2021-02-23:/archives/1133<p>After several years of hard work, DIPlib 3 is finally
<a href="https://diplib.org/news/2021/02/22/Release-v3.0.0.html">out of beta</a>.</p>
<p><a href="/archives/874">I presented the DIPlib 3 project on this blog</a> three and a half years ago,
when I discussed the advantages of the new C++ library over its C predecessor, and especially
how C++ code using DIPlib 3 …</p><p>After several years of hard work, DIPlib 3 is finally
<a href="https://diplib.org/news/2021/02/22/Release-v3.0.0.html">out of beta</a>.</p>
<p><a href="/archives/874">I presented the DIPlib 3 project on this blog</a> three and a half years ago,
when I discussed the advantages of the new C++ library over its C predecessor, and especially
how C++ code using DIPlib 3 is quite similar to MATLAB code using DIPimage (meaning it is simple!).
I also mentioned the new Python bindings, PyDIP, which I demonstrated in <a href="/archives/1132">last week’s blog post</a>.</p>
<p>For this post I thought I’d show off the image visualization tool that my friend Wouter Caarls
wrote for the DIPlib project. Prototyping Image Analysis algorithms often requires visualizing
intermediate results, which makes MATLAB or Python much more attractive than C++ for prototyping,
as those environments have many options for interactive visualization of data. DIPlib’s new
visualization tool, <a href="https://diplib.org/diplib-docs/dipviewer.html">DIPviewer</a>, fills this role
for C++, making prototyping Image Analysis algorithms directly in C++ quite easy and pleasant.</p>
<p>To visualize an image, simply call <code>dip::viewer::Show()</code>. Multiple images can be shown at the
same time. A call to <code>dip::viewer::Spin()</code> pauses execution of the program and allows interaction
with the visualized images. Here’s an example:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib/simple_file_io.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib/linear.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"dipviewer.h"</span><span class="c1"> // Add this</span><span class="cp"></span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Image</span><span class="w"> </span><span class="n">anat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">ImageRead</span><span class="p">(</span><span class="w"> </span><span class="s">"sub-1-anat-sub-1_T1w.nii"</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">viewer</span><span class="o">::</span><span class="n">Show</span><span class="p">(</span><span class="w"> </span><span class="n">anat</span><span class="p">,</span><span class="w"> </span><span class="s">"anat image"</span><span class="w"> </span><span class="p">);</span><span class="w"> </span><span class="c1">// Add this</span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Image</span><span class="w"> </span><span class="n">H</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Hessian</span><span class="p">(</span><span class="n">anat</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">viewer</span><span class="o">::</span><span class="n">Show</span><span class="p">(</span><span class="w"> </span><span class="n">H</span><span class="p">,</span><span class="w"> </span><span class="s">"Hessian"</span><span class="w"> </span><span class="p">);</span><span class="w"> </span><span class="c1">// Add this</span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">viewer</span><span class="o">::</span><span class="n">Spin</span><span class="p">();</span><span class="w"> </span><span class="c1">// Add this</span>
<span class="w"> </span><span class="c1">// ... rest of program</span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>The two images are displayed like this:</p>
<p class="centering"><img alt="Two DIPviewer windows" src="/images/dipviewer.png"></p>
<p>Many interesting things can be seen in the image above: the 3D images are shown as three
slices through the volume, one slice is the main display, the other two slices are meant
to aid navigation. The display can show images with more dimensions just as easily, and one
can choose how each of these three panels slices the image.
To the right is a histogram and a color bar, indicating the range of the image data and
the selected range for display, which can be adjusted there.
In the top-right corner are controls allowing one to adjust how the image is displayed,
including the color map.
At the bottom of the window one can read out the coordinates of the cursor, both in
pixels and in physical units, as well as the value of the pixel under the cursor.</p>
<p>The image displayed in the right window, the Hessian, is a 3x3 tensor image. The top-left
corner of the window contains a control allowing one to select which tensor component
is visualized as gray-value image. When selecting the RGB display mode, up to three
tensor components can be selected, one for each of the red, green and blue channels.</p>
<p>Finally, the two windows were linked, which can be seen by the cursor over the two
images being at the same spot. The numbers read out at the bottom of the two windows
correspond to the same pixel in the two images. When navigating in one of the windows,
the other window follows along. This is incredibly convenient when examining both
the input image and one or more intermediate results, where one wants to learn how
the image was transformed.</p>
<p>Pressing Ctrl+Shift+W closes all windows and allows the program to continue execution.</p>
<p>One more thing: Note how the example code above loads an MRI image from a file with
an <code>.nii</code> extension. I had, until very recently, not heard of this file format.
Luckily, it is one of the
<a href="https://docs.openmicroscopy.org/bio-formats/6.6.0/supported-formats.html">150 file formats</a>
that the <a href="https://www.openmicroscopy.org/bio-formats/">Bio-Formats library</a> understands.
Turns out it is the NIfTI format used by the fMRI community.
DIPlib 3 has an interface to Bio-Formats, and so I’m able to load this file through
<code>dip::ImageRead()</code>. Coincidentally, the interface to Bio-Formats in DIPlib was also
written by Wouter Caarls.</p>
<p>Thank you Wouter!</p>Moving again!2021-02-11T17:32:00-07:002021-02-11T17:32:00-07:00Cris Luengotag:www.crisluengo.net,2021-02-11:/archives/1131<p>…well, not physically. Even the URL of this blog is going to remain the same.</p>
<p>But I’m moving from Wordpress to a statically generated site (I’m using <a href="https://docs.getpelican.com/en/latest/#">Pelican</a>).</p>
<p>Some months ago I decided to turn off the comments. I was receiving way too many spam comments, and way …</p><p>…well, not physically. Even the URL of this blog is going to remain the same.</p>
<p>But I’m moving from Wordpress to a statically generated site (I’m using <a href="https://docs.getpelican.com/en/latest/#">Pelican</a>).</p>
<p>Some months ago I decided to turn off the comments. I was receiving way too many spam comments, and way too few comments
from readers. With the comments turned off, there is no longer anything dynamic about this site. What is the point of
having it stored in a database and generated on the fly for each visitor? My web host
(<a href="https://www.dreamhost.com">Dreamhost</a>) even complained at some point a few years back when one of my
posts became popular, I turned on a caching add-on in response. So now I have a complex Wordpress, with a complex
caching mechanism added on, just to serve a couple of relatively simple pages… This makes no sense.</p>
<p>Pelican allows me to write in Markdown (which I’ve been doing a lot of in the last few years), allows me to tweak the
theme locally on my computer, and I’ve even been able to use my new Python-Markdown plugin:
<a href="https://github.com/crisluengo/mdx_math_svg">mdx_math_svg</a>, which creates SVG from LaTeX equations and embeds them
into the output HTML.</p>
<p>Anyway, hopefully most links to this site will not break, but the RSS feed will stop, we’ll have an Atom feed
instead. <strong>So if you have a feed reader pointing here, please come by in a few days and set it up again.</strong>
Also, the email list will go away. <strong>You will no longer receive automated emails every time I post
something.</strong> I’m not keeping the email list. Please use the Atom feed instead.</p>
<p>The one thing I’m still torn about is losing the comments. Some posts have some interesting comments under them. Oh,
well, nothing is forever.</p>Presenting DIPlib 32017-08-20T23:10:00-06:002017-08-20T23:10:00-06:00Cris Luengotag:www.crisluengo.net,2017-08-20:/archives/874<p>DIPimage is a MATLAB toolbox for quantitative image analysis. We’ve got quite a few users, especially in academia.
However, few of those users (as far as I know) have ventured down the path of directly using DIPlib, the C library that
DIPimage is built upon. I know of two …</p><p>DIPimage is a MATLAB toolbox for quantitative image analysis. We’ve got quite a few users, especially in academia.
However, few of those users (as far as I know) have ventured down the path of directly using DIPlib, the C library that
DIPimage is built upon. I know of two people, outside of the group at Delft University of Technology where we developed
DIPlib and DIPimage, that have written C code that uses DIPlib. And that is too bad, because it’s a wonderful library.
There are two reasons for this lack of uptake: it has a very steep learning curve, and it is not open source. The second
reason makes the first one worse, because there’s very little example source code to look at for learning to use the
library.</p>
<p>Back in 2014 I started dreaming of porting DIPlib to C++, and making it open source. Modern C++ is a very expressive
language, and writing code that uses a C++ version of DIPlib doesn’t need to be much more complicated that writing the
equivalent MATLAB code. The port would allow moving some of the innovations we introduced in DIPimage into the DIPlib
library, such as tensor (vector or matrix) images, color space management, etc. I did write a first version of
the <code>dip::Image</code> class to test and learn how the library could look, and write proposals trying to convince people to
help me build it, but otherwise didn’t put much effort into the project until last year. Over the last year and a half
or so, I have invested a lot of my free time to build a whole new library infrastructure, and port over algorithms. The
work is not nearly finished, but <a href="https://github.com/DIPlib/diplib">there already is a lot there</a>, and I have been using
it at work in production code. Even though I initially set out to port algorithms unmodified, I find myself improving
code quite frequently, some algorithms are significantly faster than they were before (e.g. the Watershed, which now
uses a correct implementation of Union-Find, and the labelling algorithm (connected component analysis), which now uses
a completely different algorithm).</p>
<p>The DIPlib 3 project consists of various parts:</p>
<ol>
<li>
<p><strong>DIPlib</strong>, the C++ library. The library infrastructure is complete, and more than half of the image
processing/analysis algorithms have been ported from the old DIPlib. Most function names are the same, but I have not
tried to maintain backwards compatibility. I did
keep <a href="https://diplib.github.io/diplib-docs/changes.html">a list of changes</a>. The documentation can be found
here: <a href="https://diplib.github.io/diplib-docs/">https://diplib.github.io/diplib-docs/</a>.</p>
</li>
<li>
<p><strong>DIPviewer</strong>, an extension to DIPlib (thanks to Wouter Caarls) to display 2D and 3D images. Not yet documented.</p>
</li>
<li>
<p><strong>DIPimage</strong>, the MATLAB toolbox. I’m recreating all functions by directly calling the corresponding DIPlib
function (i.e. there’s less overhead because we’re not parsing input arguments in M-code any more), and trying to
maintain backwards compatibility as best as makes sense. At the bottom
of <a href="https://diplib.github.io/diplib-docs/changes.html">the page with changes</a> you can see what has changed. The
DIPimage GUI hasn’t been moved over yet, but all old functions that are already available in the new DIPlib have
been “ported” (maybe half of all toolbox functions, and all of the <code>dip_image</code> class methods).</p>
</li>
<li>
<p><strong>PyDIP</strong>, the Python module. Currently this is composed of a thin wrapper around most C++ functionality.
The <code>dip.Image</code> object is identical to the C++ counterpart, but also exposes its buffer so that it can be used with
e.g. NumPy functions. Conversely, it is possible to use a NumPy array (or other object that exposes its buffer)
instead of a <code>dip.Image</code> object as input to PyDIP functions. Maybe, eventually, this module will become more
“Pythonic”. Maybe we’ll create a GUI like DIPimage has. We’ll see where this goes.</p>
</li>
</ol>
<h2>Some examples</h2>
<p>I’d like to give an example of the complexity of the old DIPlib, and the simplicity of the new one. Take the following
bit of DIPimage code:</p>
<div class="highlight"><pre><span></span><code><span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">readim</span><span class="p">(</span><span class="s">'cermet'</span><span class="p">);</span><span class="w"></span>
<span class="n">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">label</span><span class="p">(</span><span class="n">a</span><span class="o"><</span><span class="mi">120</span><span class="p">);</span><span class="w"></span>
<span class="n">m</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">measure</span><span class="p">(</span><span class="n">b</span><span class="p">,</span><span class="n">a</span><span class="p">,</span><span class="s">'Size'</span><span class="p">);</span><span class="w"></span>
<span class="nb">disp</span><span class="p">(</span><span class="nb">mean</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">Size</span><span class="p">))</span><span class="w"></span>
</code></pre></div>
<p>With the old DIPlib, you would write C code like this:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"dipio.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"dip_point.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"dip_regions.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"dip_measurement.h"</span><span class="cp"></span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">argv</span><span class="p">[]</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">DIP_FN_DECLARE</span><span class="p">(</span><span class="w"> </span><span class="s">"main"</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">dip_Resources</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">dip_Image</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">dip_String</span><span class="w"> </span><span class="n">filename</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">dip_IntegerArray</span><span class="w"> </span><span class="n">featureID</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">dip_Measurement</span><span class="w"> </span><span class="n">m</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_Initialise</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dipio_Initialise</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_ResourcesNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">rg</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_ImageNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_StringNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">filename</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="s">"cermet"</span><span class="p">,</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dipio_ImageRead</span><span class="p">(</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">filename</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_ImageNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_Threshold</span><span class="p">(</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="mi">120</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">DIP_TRUE</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_Label</span><span class="p">(</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_IntegerArrayNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">featureID</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">dip_FeatureSizeID</span><span class="p">(),</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_MeasurementNew</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXJ</span><span class="p">(</span><span class="w"> </span><span class="n">dip_Measure</span><span class="p">(</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">featureID</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="cm">/* Omitted lots of code here, because there are no convenience functions</span>
<span class="cm"> * to compute the mean of a measurement. Accessing measurement values is</span>
<span class="cm"> * convoluted! */</span><span class="w"></span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="w"> </span><span class="s">"%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">mean_size</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="nl">dip_error</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXC</span><span class="p">(</span><span class="w"> </span><span class="n">dip_ResourcesFree</span><span class="p">(</span><span class="w"> </span><span class="o">&</span><span class="n">rg</span><span class="w"> </span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXC</span><span class="p">(</span><span class="w"> </span><span class="n">dipio_Exit</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">DIPXC</span><span class="p">(</span><span class="w"> </span><span class="n">dip_Exit</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">dip_ErrorWrite</span><span class="p">(</span><span class="w"> </span><span class="n">error</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">stderr</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>With the new DIPlib, your C++ code would look like this:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib/file_io.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib/regions.h"</span><span class="cp"></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"diplib/measurement.h"</span><span class="cp"></span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">ImageReadICS</span><span class="p">(</span><span class="w"> </span><span class="s">"cermet"</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Label</span><span class="p">(</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">120</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">MeasurementTool</span><span class="w"> </span><span class="n">measurementTool</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">auto</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">measurementTool</span><span class="p">.</span><span class="n">Measure</span><span class="p">(</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s">"Size"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="w"> </span><span class="o"><<</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Mean</span><span class="p">(</span><span class="w"> </span><span class="n">m</span><span class="p">[</span><span class="w"> </span><span class="s">"Size"</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>Note that a lot of the typing on the old DIPlib is related to memory management and error management. C++ takes care of
these things automatically. C++ also allows one to not specify types of variables, they are determined from the return
type of the functions. C++ also supports default parameters (meaning not all parameters need to be given in each
function call), automatic casting (meaning it is not necessary to explicitly build a <code>dip_String</code> object from the file
name string), and overloaded operators (comparison operator for thresholding, indexing operator to retrieve a
measurement result). Besides needing to specify some include files, create a <code>dip::MeasurementTool</code> object, and specify
the <code>dip</code> namespace, the code is identical to that in DIPimage.</p>
<p>Here’s a quick example using tensor images. In DIPimage you can write:</p>
<div class="highlight"><pre><span></span><code><span class="n">g</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">gradient</span><span class="p">(</span><span class="n">img</span><span class="p">);</span><span class="w"></span>
<span class="n">S</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">smooth</span><span class="p">(</span><span class="n">g</span><span class="o">*</span><span class="n">g</span><span class="o">'</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<p>This creates <code>S</code>, an image where each pixel is a 2x2 matrix (or 3x3 for a 3D image), and is known as the structure
tensor. The eigenvalues and eigenvectors of each pixel in this image give information about the local structure. With
the new DIPlib you can write the same thing:</p>
<div class="highlight"><pre><span></span><code><span class="k">auto</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Gradient</span><span class="p">(</span><span class="w"> </span><span class="n">img</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="k">auto</span><span class="w"> </span><span class="n">S</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Gauss</span><span class="p">(</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">dip</span><span class="o">::</span><span class="n">Transpose</span><span class="p">(</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<p>The difference here is that <code>S</code> is explicitly a symmetric matrix image, where only three of the four components (6 of
the 9 in 3D) are computed and stored. The multiplication operator recognizes that its two input arguments are transposed
versions of each other, and thus knows to produce a symmetric output.</p>
<p>These examples were not picked because they are simple to write in the new DIPlib, I could have picked any example and
obtain the same effect. The C++ code is not much more complicated than the DIPimage code. This is by design, and I’m
very excited about it. This is the reason it is now possible to rewrite most of DIPimage as direct calls to DIPlib, and
create PyDIP as a thin wrapper.</p>
<h2>The future</h2>
<p>Not all functions have been ported yet. There is also a bunch of functionality in the old DIPimage that I’d like to move
into the C++ library, and a bunch of functionality that is simply missing altogether and I’d like to implement or
borrow. But it is already possible to use the library and the toolbox.</p>
<p>If you’re interested in any parts of this project, feel free to:</p>
<ul>
<li>Let me know, so I can keep you updated.</li>
<li><a href="https://github.com/DIPlib/diplib">Download</a>, compile and use the library, MATLAB toolbox or Python module, and let me
know what you think.</li>
<li><a href="https://github.com/DIPlib/diplib/blob/master/src/documentation/workplan.md">Contribute!</a> Any kind of help is
appreciated.</li>
</ul>Moving2015-11-26T18:07:00-07:002015-11-26T18:07:00-07:00Cris Luengotag:www.crisluengo.net,2015-11-26:/archives/815<p>After seven years at the <a href="http://www.cb.uu.se/">Centre for Image Analysis</a> at Uppsala University, in Sweden, it is time
again to move on. I am sad to leave this great position, my wonderful colleagues, and the fantastic Uppsala students.
But such is life.</p>
<p>For the first time in my life, I am …</p><p>After seven years at the <a href="http://www.cb.uu.se/">Centre for Image Analysis</a> at Uppsala University, in Sweden, it is time
again to move on. I am sad to leave this great position, my wonderful colleagues, and the fantastic Uppsala students.
But such is life.</p>
<p>For the first time in my life, I am
now <a href="http://flagshipbio.com/news/flagship-biosciences-expands-tissue-image-analysis-leadership/">working for a company</a>.
I am happy to say that I still have wonderful colleagues. I will probably miss having students around, but I certainly
will not miss the grant writing.</p>
<p>I bother to mention this on this blog because, with my move away from Uppsala University, this blog is also relocating!
The new URL is <a href="https://www.crisluengo.net">crisluengo.net</a>. Please update your bookmarks!</p>DIPimage 2.6 released2014-04-14T22:08:00-06:002014-04-14T22:08:00-06:00Cris Luengotag:www.crisluengo.net,2014-04-14:/archives/651<p>Today we released version 2.6 of DIPimage and DIPlib. The <a href="https://diplib.org/news/2014/04/14/Version-2.6.html">change list</a> is
rather short. There are two items that I think are important: 1) We fixed a bug that caused an unnecessary copy of the
output image(s) in the DIPlib-MEX interface, slowing down functions especially for large …</p><p>Today we released version 2.6 of DIPimage and DIPlib. The <a href="https://diplib.org/news/2014/04/14/Version-2.6.html">change list</a> is
rather short. There are two items that I think are important: 1) We fixed a bug that caused an unnecessary copy of the
output image(s) in the DIPlib-MEX interface, slowing down functions especially for large images. 2) We introduced a new
setting to automatically make use of
a <a href="/archives/607" title="DIPimage 2.5.1 released">feature introduced in the previous release</a>.</p>
<h2>The bug</h2>
<p>Thanks to some attentive users, and some nifty sleuthing by a colleague, we were able to track down a curious bug: it
seems that, when the DIPlib-MEX interface created a <code>dip_image</code> object, the data array inside the object would be copied
while exiting the MEX-file. I am very sure that this did not happen when we first developed the interface (way back in
1999, when the newest MATLAB version was 5.3!). I don’t know when this “new behaviour” was introduced, but it certainly
wasn’t announced in the release notes. We now avoid the copy by creating an empty object, and then inserting the data
array into it.</p>
<p>For simple functions working on large images, the impact of this bug was huge. For example, a simple arithmetic
operation on a large image is completely limited by the speed at which memory can be accessed. Computing the division
with a constant takes just as much time as making a copy of the data. Let’s test this:</p>
<div class="highlight"><pre><span></span><code><span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">repmat</span><span class="p">(</span><span class="n">readim</span><span class="p">,[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">20</span><span class="p">]);</span><span class="w"></span>
<span class="nb">timeit</span><span class="p">(@()(</span><span class="n">a</span><span class="o">/</span><span class="mi">2</span><span class="p">))</span><span class="w"></span>
</code></pre></div>
<div class="output highlight"><pre><span></span><code><span class="go">ans =</span>
<span class="go"> 1.3306</span>
</code></pre></div>
<p>I created a large image by replicating a standard image; the size of <code>a</code> is 1536×1792×20 = 55 Mpixels, of type <code>uint8</code>.
Then I time the division of this image by 2. This produces a single-precision floating-point (<code>sfloat</code>) image. The
computation time was 1.33 s with DIPimage 2.5.1. Let’s restart MATLAB with the new version of DIPimage, and repeat the
experiment:</p>
<div class="highlight"><pre><span></span><code><span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">repmat</span><span class="p">(</span><span class="n">readim</span><span class="p">,[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">20</span><span class="p">]);</span><span class="w"></span>
<span class="nb">timeit</span><span class="p">(@()(</span><span class="n">a</span><span class="o">/</span><span class="mi">2</span><span class="p">))</span><span class="w"></span>
</code></pre></div>
<div class="output highlight"><pre><span></span><code><span class="go">ans =</span>
<span class="go"> 0.6345</span>
</code></pre></div>
<p>As expected, the computation time is about half. For more complex functions, the difference might not be as dramatic,
but it certainly is worth upgrading!</p>
<h2>A new setting</h2>
<p>In the previous version of
DIPimage <a href="/archives/607" title="DIPimage 2.5.1 released">we introduced the ability to do arithmetic operations and specify the data type of the output image</a>
. By default, DIPimage usually chooses <code>sfloat</code> as the output data type. This is to avoid unexpected results, as keeping
the data type of the image typically leads to rounding errors or overflow, which can simply be avoided by choosing a
floating-point data type. Note that this default behaviour is usually what is expected, and frees the user from having
to think about what data type to use.</p>
<p>However, sometimes advanced users want to control the output data type. For example, when working with very large
images, it can be convenient (saves time, avoids memory problems) to use only <code>uint8</code> or <code>uint16</code> data types (a <code>sfloat</code>
image takes up 4 times as much space as a <code>uint8</code> image). In the previous version of DIPimage, one could then type, for
example, <code>dip_arith(a,2,'div','uint8')</code> instead of <code>a/2</code>. Not very elegant, not very readable, but hey! it saves up
memory space!</p>
<p>With the new version of DIPimage comes a new setting, called <code>'KeepDataType'</code>. It causes the output of arithmetic
operations to be the same as that of the input images. If the types are different, then the larger of the two types is
chosen, except if one of the inputs is a MATLAB array, as we only look at the data type of the image. This simplifies
the use of the memory-saving feature, as the choice made is usually a good choice. However, note that overflow and
rounding errors can (and will) occur! Let’s try the same division as before, but using the new setting:</p>
<div class="highlight"><pre><span></span><code><span class="n">dipsetpref</span><span class="w"> </span><span class="s">KeepDataType</span><span class="w"> </span><span class="s">on</span><span class="w"></span>
<span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">readim</span><span class="p">;</span><span class="w"></span>
<span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">repmat</span><span class="p">(</span><span class="n">a</span><span class="p">,[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">20</span><span class="p">]);</span><span class="w"></span>
<span class="nb">timeit</span><span class="p">(@()(</span><span class="n">a</span><span class="o">/</span><span class="mi">2</span><span class="p">))</span><span class="w"></span>
</code></pre></div>
<div class="output highlight"><pre><span></span><code><span class="go">ans =</span>
<span class="go"> 0.3595</span>
</code></pre></div>
<p>As you can see, since the output image is only a fourth the size it was before, it takes a lot less time to write the
output to memory. But again, be careful when using this feature! You will get unexpected output if you’re not careful!</p>DIPimage 2.5.1 released2013-10-04T16:13:00-06:002013-10-04T16:13:00-06:00Cris Luengotag:www.crisluengo.net,2013-10-04:/archives/607<p>We recently released a bugfix for DIPimage release 2.5. Since I didn’t announce the release of 2.5 here, this might be a
good opportunity to review some of the improvements in 2.5.</p>
<h2>The Fourier transform</h2>
<p>One of the most important changes, in my mind, was also …</p><p>We recently released a bugfix for DIPimage release 2.5. Since I didn’t announce the release of 2.5 here, this might be a
good opportunity to review some of the improvements in 2.5.</p>
<h2>The Fourier transform</h2>
<p>One of the most important changes, in my mind, was also quite simple to implement: we added an option to use MATLAB’s
built-in FFT functions instead of the one in DIPlib. MATLAB’s built-in functions use the FFTW library (“Fastest Fourier
Transform in the West”). Here I compare the execution time on a reasonably large image (2048<sup>2</sup> pixels) and three
cropped versions of it: 2000<sup>2</sup>, 2001<sup>2</sup> and 2003<sup>2</sup> pixels. This last image size is a prime number. If you know about
the Fast Fourier Transform algorithm, you know that sizes that are a power of two are the fastest to compute because you
can halve the size until you have a single pixel left. A round number like 2000 is easily decomposed into favourable
sections, and a prime number requires the most complex code to efficiently compute the FT. Not surprisingly, it is for
the prime image size that the DIPlib code is the least efficient (FFTW is two orders of magnitude faster!); but also for
the nice image sizes, DIPlib needs about three times as much time as FFTW to do the computations. Here is the code and a
little graph comparing the results:</p>
<div class="highlight"><pre><span></span><code><span class="nb">clear</span><span class="w"> </span><span class="nb">all</span><span class="w"></span>
<span class="n">a</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">repmat</span><span class="p">(</span><span class="n">readim</span><span class="p">,[</span><span class="mi">8</span><span class="p">,</span><span class="mi">8</span><span class="p">]);</span><span class="w"> </span><span class="c">% a power of two number of pixels</span><span class="w"></span>
<span class="n">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">a</span><span class="p">(</span><span class="mi">0</span><span class="p">:</span><span class="mi">1999</span><span class="p">,</span><span class="mi">0</span><span class="p">:</span><span class="mi">1999</span><span class="p">);</span><span class="w"> </span><span class="c">% a round number of pixels</span><span class="w"></span>
<span class="n">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">a</span><span class="p">(</span><span class="mi">0</span><span class="p">:</span><span class="mi">2000</span><span class="p">,</span><span class="mi">0</span><span class="p">:</span><span class="mi">2000</span><span class="p">);</span><span class="w"> </span><span class="c">% an odd number of pixels</span><span class="w"></span>
<span class="n">d</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">a</span><span class="p">(</span><span class="mi">0</span><span class="p">:</span><span class="mi">2002</span><span class="p">,</span><span class="mi">0</span><span class="p">:</span><span class="mi">2002</span><span class="p">);</span><span class="w"> </span><span class="c">% a prime number of pixels</span><span class="w"></span>
<span class="n">t</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">zeros</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">);</span><span class="w"></span>
<span class="n">dipsetpref</span><span class="p">(</span><span class="s">'FFTtype'</span><span class="p">,</span><span class="s">'diplib'</span><span class="p">)</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">a</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">b</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">c</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">d</span><span class="p">));</span><span class="w"></span>
<span class="n">dipsetpref</span><span class="p">(</span><span class="s">'FFTtype'</span><span class="p">,</span><span class="s">'fftw'</span><span class="p">)</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">a</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">b</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">c</span><span class="p">));</span><span class="w"></span>
<span class="n">t</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">timeit</span><span class="p">(@()</span><span class="w"> </span><span class="n">ft</span><span class="p">(</span><span class="n">d</span><span class="p">));</span><span class="w"></span>
<span class="n">clf</span><span class="w"></span>
<span class="s">set(gcf,'position',[300,300,340,300])</span><span class="w"></span>
<span class="nb">set</span><span class="p">(</span><span class="nb">gca</span><span class="p">,</span><span class="s">'fontsize'</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span><span class="s">'linewidth'</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="nb">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="s">'ko-'</span><span class="p">,</span><span class="s">'linewidth'</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="nb">set</span><span class="p">(</span><span class="nb">gca</span><span class="p">,</span><span class="s">'xtick'</span><span class="p">,</span><span class="mi">1</span><span class="p">:</span><span class="mi">4</span><span class="p">,</span><span class="s">'xticklabel'</span><span class="p">,{</span><span class="nb">size</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><span class="nb">size</span><span class="p">(</span><span class="n">b</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><span class="nb">size</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><span class="nb">size</span><span class="p">(</span><span class="n">d</span><span class="p">,</span><span class="mi">1</span><span class="p">)},</span><span class="s">'xlim'</span><span class="p">,[</span><span class="mf">0.8</span><span class="p">,</span><span class="mf">4.2</span><span class="p">])</span><span class="w"></span>
<span class="nb">xlabel</span><span class="p">(</span><span class="s">'image size (px)'</span><span class="p">)</span><span class="w"></span>
<span class="nb">ylabel</span><span class="p">(</span><span class="s">'time (s)'</span><span class="p">)</span><span class="w"></span>
<span class="nb">set</span><span class="p">(</span><span class="nb">gca</span><span class="p">,</span><span class="s">'yscale'</span><span class="p">,</span><span class="s">'log'</span><span class="p">,</span><span class="s">'ylim'</span><span class="p">,[</span><span class="mf">0.1</span><span class="p">,</span><span class="mi">100</span><span class="p">],</span><span class="s">'yticklabel'</span><span class="p">,[</span><span class="mf">0.1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">100</span><span class="p">]);</span><span class="w"></span>
<span class="n">box</span><span class="w"> </span><span class="s">off</span><span class="w"></span>
<span class="nb">text</span><span class="p">(</span><span class="mf">4.1</span><span class="p">,</span><span class="n">t</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><span class="s">'diplib'</span><span class="p">,</span><span class="s">'fontsize'</span><span class="p">,</span><span class="mi">12</span><span class="p">)</span><span class="w"></span>
<span class="nb">text</span><span class="p">(</span><span class="mf">4.1</span><span class="p">,</span><span class="n">t</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">),</span><span class="s">'fftw'</span><span class="p">,</span><span class="s">'fontsize'</span><span class="p">,</span><span class="mi">12</span><span class="p">)</span><span class="w"></span>
<span class="nb">print</span><span class="w"> </span><span class="o">-</span><span class="n">r0</span><span class="w"> </span><span class="o">-</span><span class="n">dpng</span><span class="w"> </span><span class="n">fft_test</span><span class="p">.</span><span class="n">png</span><span class="w"></span>
</code></pre></div>
<p class="centering"><img alt="speed comparison graph" src="/images/fft_test.png"></p>
<p><strong>Note</strong> that I’ve used the function <code>timeit</code>, which is included with MATLAB since version 2013b (just released).
<strike>If you have an older version of MATLAB (like I do), get this function from the MATLAB File Exchange.</strike></p>
<h2>Arithmetic</h2>
<p>Several internal changes to how arithmetic operations are resolved (not the actual code that does the computations) were
introduced in release 2.5. These changes also introduced some unintended changes in behaviour (i.e. bugs), which
were <a href="https://diplib.org/news/2013/09/23/Version-2.5.1.html">fixed in release 2.5.1</a>. One of the problems that prompted
us to make these changes was that it was impossible to make a scalar image of type other than <code>'sfloat'</code> (even
when <code>'dfloat'</code> was explicitly requested, the number was converted to single precision first, then converted back to
double precision, meaning that precision was lost). A consequence of the changes is that the function <code>dip_arith</code>, which
is called to compute the output of arithmetic operations, now has an additional input parameter to select the data type
of the output. You can exploit this new parameter to avoid the default behaviour where the output of an arithmetic
operation is always a floating point type. For example, consider <code>a</code> and <code>b</code> two images of type <code>'uint8'</code> (8-bit
unsigned integers). The code</p>
<div class="highlight"><pre><span></span><code><span class="n">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">a</span><span class="o">+</span><span class="n">b</span><span class="p">;</span><span class="w"></span>
</code></pre></div>
<p>produces an image <code>c</code> that is of type <code>'sfloat'</code>. This is an easy way of avoiding overflow errors, as well as rounding
errors in the multiplication and division. Now you can instead type</p>
<div class="highlight"><pre><span></span><code><span class="n">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">dip_arith</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">,</span><span class="s">'add'</span><span class="p">,</span><span class="s">'uint8'</span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<p>and obtain an image <code>c</code> that is of type <code>'uint8'</code>. The main advantage here, of course, is the memory saved (1 byte per
pixel instead of 4 bytes per pixel).</p>
<p>(PS: Actually, <code>dip_arith</code> is a new function. Previously we had <code>dip_add</code>, <code>dip_mul</code>, etc., but the underlying DIPlib
code called is the same.)</p>
<h2>Other improvements</h2>
<p>We have a few more improvements, see the full changelists for <a href="https://diplib.org/news/2013/06/12/Version-2.5.html">release 2.5</a>
and <a href="https://diplib.org/news/2013/09/23/Version-2.5.1.html">release 2.5.1</a>. For example, reading TIFF stacks as a
single 3D image is much faster now, and we added Lanczos interpolation to the options in <code>resample</code> and <code>rotation</code> (the
options are called <code>'lanczos2'</code>, <code>'lanczos3'</code>, <code>'lanczos4'</code>, etc. for the various useful values of parameter <em>a</em> in the
method).</p>ISMM 20132012-06-30T16:48:00-06:002012-06-30T16:48:00-06:00Cris Luengotag:www.crisluengo.net,2012-06-30:/archives/478<p>We are going to be organizing the 11th conference in a series dedicated to Mathematical Morphology. This is the main
conference in the field, and many of the ideas within Mathematical Morphology were first presented there. I can’t count
the number of times the proceedings for this series is …</p><p>We are going to be organizing the 11th conference in a series dedicated to Mathematical Morphology. This is the main
conference in the field, and many of the ideas within Mathematical Morphology were first presented there. I can’t count
the number of times the proceedings for this series is mentioned in the reference list of my papers! The first call for
papers just went out, I’m copying it here for anyone interested. For more information, visit
the <a href="http://ismm.cb.uu.se/">ISMM 2013 website</a>.</p>
<div class="highlight"><pre><span></span><code> ISMM 2013
11th International Symposium
on Mathematical Morphology
May 27-29, 2013
Uppsala, Sweden
http://ismm.cb.uu.se
The International Symposium on Mathematical Morphology (ISMM) has
established itself as the main scientific event in the field. This
edition of ISMM will be the eleventh in the series initiated in 1993
in Barcelona, and has never been this far North before. The goal of
the conference is to bring together researchers, students, and
practitioners of Mathematical Morphology, to present and discuss new
advances in the field, be they purely theoretical developments or
novel applications.
Proceedings will be published in Springer's Lecture Notes in Computer
Science series.
Main topics of interest:
------------------------
* Theory: Morphology on complete lattices and semilattices,
Representation of morphological operators, Fuzzy morphology,
Connectivity theory, Connected operators, Morphology on graphs.
* Filtering: Colour and multi-channel morphology, Geodesic
transformations, Adaptive morphology, Attribute filtering, Image
simplification.
* Segmentation: Watershed segmentation, Hierarchical segmentation,
Colour image segmentation, Texture segmentation, Clustering of
spatial data.
* Geometry and Topology: Discrete geometry, Metrics and distance
transforms, Random sets and geometrical probability, Shape analysis.
* Algorithms and Architectures: Efficient implementations, Data
structures for morphology, Performance evaluation of algorithms, GPU
implementations.
* Related methodology: Level set methods, Morphological PDEs,
Wavelets, Scale spaces.
* Applications: Geoscience and remote sensing, Biomedical imaging,
Materials science, Data analysis, Document processing, Content-based
information retrieval, Video surveillance, Industrial control,
Visualisation, etc.
Important dates:
----------------
Submission deadline: December 9, 2012
Notification of acceptance: February 8, 2013
Camera ready paper: March 10, 2013
Conference: May 27-29, 2013
Note: the submission deadline is final and will not be extended!
Organizing Committee:
---------------------
Gunilla Borgefors, General Chair
Christer Kiselman, Invited Speakers' Chair and Advisor
Cris Luengo, Programme Chair
Robin Strand, Local Organisation Chair
Vlada Curic, Assistant
Venue:
------
Ångström Laboratory of Uppsala University, Uppsala, Sweden.
</code></pre></div>