DIPlib 3.2.0 released
We made a new release for DIPlib this week, version 3.2.0
(see the change log).
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 dip.Histogram()
and dip.LookupTable()
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.
In previous versions, dip.Histogram()
and dip.LookupTable()
were functions that used
the dip::Histogram
and the
dip::LookupTable
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.
What this means for old code is that dip.Histogram()
no longer returns a tuple with
an array of bin values and an array of bin centers, but returns a dip.Histogram
object.
Likewise, dip.LookupTable()
no longer is a function that applies a lookup table to an
image, returning an image, but is a constructor that returns a dip.LookupTable
object.
To keep old code working there are two options:
-
After importing the
diplib
package, assigndip.Histogram = dip.Histogram_old
and/ordip.LookupTable = dip.LookupTable_old
, to recover the behavior of previous versions. -
Rewrite your code to use the new classes. Below I’ll demonstrate how these classes can be used, and what the advantages are.
dip.Histogram
The dip.Histogram
class has constructors similar to
those in the C++ library,
using an object of class dip.Histogram.Configuration
, or an array of them for
multi-dimensional histograms, to define the histogram parameters. In C++, we can construct
a histogram as follows:
dip::Histogram hist1( img, {}, { 0.0, 255.0, 100 } );
dip::Histogram hist2( img, {}, { 0.0, 255.0, 1.7 } );
resulting in a histogram hist1
with bounds [0.0, 255.0] and 100 bins, and a histogram
hist2
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:
hist1 = dip.Histogram(img, None, dip.Histogram.Configuration(lowerBound=0, upperBound=255, nBins=100))
hist2 = dip.Histogram(img, None, dip.Histogram.Configuration(lowerBound=0, upperBound=255, binSize=1.7))
This is obviously much more verbose. This is why we added a constructor that takes the parameters as individual values:
hist1 = dip.Histogram(img, bounds=(0, 255), nBins=100)
This constructor cannot be used when one wants to set the bin size explicitly. Also when
other configuration options are needed, an explicit dip.Histogram.Configuration
object
should be constructed. On the other hand, the dip.Histogram.Configuration
object is
useful when constructing multiple histograms that must be comparable.
Old code that looked like this:
hist, bins = dip.Histogram(img, nBins=100) # old syntax
plt.plot(bins, hist) # import matplotlib.pyplot as plt
can now be written as:
hist = dip.Histogram(img, nBins=100)
plt.plot(hist.BinCenters(), hist.GetImage())
or simply as:
dip.Histogram(img, nBins=100).Show()
Besides methods to inspect the histogram, there are methods like Cumulative()
, and
Smooth()
that transform the histogram, or GetMarginal()
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.
For example, this is the way to apply k-means clustering to the intensities of an image:
img = dip.ImageRead('DIP.tif') # from here: https://github.com/DIPlib/diplib/blob/master/examples/DIP.tif
img.Show()
hist = dip.Histogram(img, nBins=100)
hist = dip.KMeansClustering(hist, 3) # 3 clusters
lab = hist.ReverseLookup(img)
lab.Show('labels')
(note that the function dip.KMeansClustering
, when applied to an image, clusters
the image spatially, not its intensities as we did here.)
There are also two functions, dip.EqualizationLookupTable()
and dip.MatchingLookupTable()
,
that return a dip.LookupTable
object from respectively one and two input histograms.
dip.LookupTable
The dip.LookupTable
class is much simpler than the dip.Histogram
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 Apply
.
Here we use the dip.LookupTable
class to paint each label in the lab
image we created
above with the average color of each
msr = dip.MeasurementTool.Measure(lab, img, ['Mean'])
colors = dip.Image(msr['Mean'], tensor_axis=1)
colors.SetColorSpace('srgb')
lut = dip.LookupTable(colors, [1,2,3]) # indices in lab start at 1
lut.Convert('UINT8')
out = lut.Apply(lab)
out.Show()
Although the above could have been done just as easily with the dip.LookupTable()
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
dip.EqualizationLookupTable
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 dip.HistogramEqualization()
because it makes it possible
to examine the mapping applied, and apply the same mapping to other images. For example,
img = dip.ImageRead('examples/trui') # from here: https://github.com/DIPlib/diplib/blob/master/examples/trui.ics
hist = dip.Histogram(img)
lut = dip.EqualizationLookupTable(hist)
imgeq = lut.Apply(img1) # histogram equalized
lut.Apply(np.arange(0, 256)).Show()
img2 = dip.ImageRead('examples/cermet') # from here: https://github.com/DIPlib/diplib/blob/master/examples/cermet.ics
img2eq = lut.Apply(img2) # histogram mapped in the same way, not equalized
Questions or comments on this topic?
Join the discussion on
LinkedIn