<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blosc Home Page  (Posts about blosc2 python user-defined filters codecs)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/categories/blosc2-python-user-defined-filters-codecs.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:blosc@blosc.org"&gt;The Blosc Developers&lt;/a&gt; </copyright><lastBuildDate>Sun, 19 Jul 2026 11:09:13 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>User Defined Pipeline for Python-Blosc2</title><link>https://blosc.org/posts/python-blosc2-pipeline/</link><dc:creator>Marta Iborra</dc:creator><description>&lt;p&gt;The Blosc Development Team is happy to announce that the latest version of  &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2"&gt;Python-Blosc2&lt;/a&gt; allows user-defined Python functions all throughout its compression pipeline: you can use Python for building prefilters, postfilters, filters, codecs for Blosc2 and explore all its capabilities.  And if done correctly (by using e.g. NumPy, numba, numexpr...), most of the time you won't even need to translate those into C for speed.&lt;/p&gt;
&lt;section id="the-blosc2-pipeline"&gt;
&lt;h2&gt;The Blosc2 pipeline&lt;/h2&gt;
&lt;p&gt;The Blosc2 pipeline includes all the functions that are applied to the data until it is finally compressed (and decompressed back). As can be seen in the image below, during compression the first function that can be applied to the data is the prefilter (if any), then the filters pipeline (with a maximum of six filters) and, last but not least, the codec itself. For decompressing, the order will be the other way around: first the codec, then the filters pipeline and finally the postfilter (if any).&lt;/p&gt;
&lt;img alt="blosc2-pipeline" class="align-center" src="https://blosc.org/images/blosc2-pipeline/blosc2-pipeline.png" style="width: 50%;"&gt;
&lt;/section&gt;
&lt;section id="defining-prefilters-and-postfilters"&gt;
&lt;h2&gt;Defining prefilters and postfilters&lt;/h2&gt;
&lt;p&gt;A prefilter is a function that is applied to the &lt;cite&gt;SChunk&lt;/cite&gt; each time you add data into it (e.g. when appending or updating). It is executed for each data block and receives three parameters: &lt;cite&gt;input&lt;/cite&gt;, &lt;cite&gt;output&lt;/cite&gt; and &lt;cite&gt;offset&lt;/cite&gt;. For convenience, the input and output are presented as NumPy arrays; the former is a view of the input data and the later is an empty NumPy array that must be filled (this is actually what the first filter in the filters pipeline will receive). Regarding the offset, it is an integer which indicates where the corresponding block begins inside the &lt;cite&gt;SChunk&lt;/cite&gt; container.&lt;/p&gt;
&lt;p&gt;You can easily set a prefilter to a specific SChunk through a decorator.  For example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk = blosc2.SChunk()
@schunk.prefilter(np.int64, np.float64)
def pref(input, output, offset):
    output[:] = input - np.pi + offset&lt;/pre&gt;
&lt;p&gt;This decorator requires the data types for the input (original data) and output NumPy arrays, which must be of same itemsize.&lt;/p&gt;
&lt;p&gt;If you do not want the prefilter to be applied anymore, you can always remove it:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk.remove_prefilter("pref")&lt;/pre&gt;
&lt;p&gt;As for the postfilters, they are applied at the end of the pipeline during decompression. A postfilter receives the same parameters as the prefilter and can be set in the same way:&lt;/p&gt;
&lt;pre class="literal-block"&gt;@schunk.postfilter(np.float64, np.int64)
def postf(input, output, offset):
    output[:] = input + np.pi - offset&lt;/pre&gt;
&lt;p&gt;In this case, the input data is the one from the buffer returned by the filter pipeline, and the output data type should be the same as the original data (for a good round-trip).&lt;/p&gt;
&lt;p&gt;You can also remove postfilters whenever you want:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk.remove_postfilter("postf")&lt;/pre&gt;
&lt;section id="fillers"&gt;
&lt;h3&gt;Fillers&lt;/h3&gt;
&lt;p&gt;Before we move onto the next step in the pipeline, we need to introduce the fillers. A filler is similar to a prefilter but with a twist. It is used to fill an empty &lt;cite&gt;SChunk&lt;/cite&gt; and you can pass to it any extra parameter you want, as long as it is a NumPy array, SChunk or Python Scalar. All these extra parameters will arrive to the filler function as a tuple containing just the corresponding block slice for each parameter (except for the scalars, that are passed untouched). To declare a filter, you will also need to pass the inputs along with its data type.  For example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;@schunk.filler(((schunk0, dtype0), (ndarray1, dtype1), (py_scalar3, dtype2)), schunk_dtype)
def filler(inputs_tuple, output, offset):
    output[:] = inputs_tuple[0] - inputs_tuple[1] * inputs_tuple[2]&lt;/pre&gt;
&lt;p&gt;This will automatically append the data to the &lt;cite&gt;schunk&lt;/cite&gt;, but applying the filler function first. After that the &lt;cite&gt;schunk&lt;/cite&gt; would be completely filled, the filler will be de-registered, so the next time you update some data the it would not be executed; a filler is meant to build new &lt;cite&gt;SChunk&lt;/cite&gt; objects from other containers.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="user-defined-filters-and-codecs"&gt;
&lt;h2&gt;User-defined filters and codecs&lt;/h2&gt;
&lt;p&gt;The main difference between prefilters/postfilters and their filters/codecs counterparts is that the former ones are meant to run for an specific &lt;cite&gt;SChunk&lt;/cite&gt; instance, whereas the later can be locally registered and hence, used in any &lt;cite&gt;SChunk&lt;/cite&gt;.&lt;/p&gt;
&lt;p&gt;Let's start describing the user-defined filters. A filter is composed by two functions: one for the compression process (&lt;em&gt;forward&lt;/em&gt;), and another one for the decompression process (&lt;em&gt;backward&lt;/em&gt;). Such functions receive the &lt;cite&gt;input&lt;/cite&gt; and &lt;cite&gt;output&lt;/cite&gt; as NumPy arrays of type &lt;cite&gt;uint8&lt;/cite&gt; (bytes), plus the filter &lt;cite&gt;meta&lt;/cite&gt; and the SChunk instance to which the data belongs to. The &lt;em&gt;forward&lt;/em&gt; function will fill the &lt;cite&gt;output&lt;/cite&gt; with the modified data from &lt;cite&gt;input&lt;/cite&gt;. The &lt;em&gt;backward&lt;/em&gt; will be responsible of reversing the changes done by &lt;em&gt;forward&lt;/em&gt; so that the data returned at the end of the decompression should be the same as the one received at the beginning of the compression. Check the drawing below:&lt;/p&gt;
&lt;img alt="forward" src="https://blosc.org/images/blosc2-pipeline/forward.png" style="width: 35%;"&gt;
&lt;img alt="backward" src="https://blosc.org/images/blosc2-pipeline/backward.png" style="width: 35%;"&gt;
&lt;p&gt;So, once we have the pair of forward and backward functions defined, they can be registered locally associating to a filter ID between 160 and 255:&lt;/p&gt;
&lt;pre class="literal-block"&gt;blosc2.register_filter(id, forward, backward)&lt;/pre&gt;
&lt;p&gt;Now, we can use the user-defined filter in any &lt;cite&gt;SChunk&lt;/cite&gt; instance by choosing the new local ID in the filters pipeline:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk.cparams = {"filters": [id], "filters_meta": [meta]}&lt;/pre&gt;
&lt;p&gt;Regarding the user-defined codecs, they do not differ too much from its filter counterparts. The main difference is that, because their goal is to actually compress data, the corresponding functions (in this case &lt;em&gt;encoder&lt;/em&gt; and &lt;em&gt;decoder&lt;/em&gt;) will need to return the size in bytes of the compressed/decompressed data respectively. This time the scheme would be:&lt;/p&gt;
&lt;img alt="encoder" src="https://blosc.org/images/blosc2-pipeline/encoder.png" style="width: 45%;"&gt;
&lt;img alt="decoder" src="https://blosc.org/images/blosc2-pipeline/decoder.png" style="width: 45%;"&gt;
&lt;p&gt;To register a codec, you name it, assign an ID to it and pass the user-defined functions:&lt;/p&gt;
&lt;pre class="literal-block"&gt;blosc2.register_codec(codec_name, id, encoder, decoder)&lt;/pre&gt;
&lt;p&gt;And to use it you just use its ID in the cparams:&lt;/p&gt;
&lt;pre class="literal-block"&gt;schunk.cparams = {"codec": id, "codec_meta": meta}&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="final-words"&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;We have seen how easily you can define your own filters and codecs for the Blosc2 compression pipeline.  They are very easy to use because they conveniently wrap input and output data as NumPy arrays.  Now, you can start experimenting with different filter/compression algorithms straight from Python.  You can even come with a library of such filters/codecs that can be used in all your data pipeline processing.  Welcome to compression made easy!&lt;/p&gt;
&lt;p&gt;See more examples in the &lt;a class="reference external" href="https://github.com/Blosc/python-blosc2/tree/main/examples"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Find the complete documentation at: &lt;a class="reference external" href="https://www.blosc.org/python-blosc2/python-blosc2.html"&gt;https://www.blosc.org/python-blosc2/python-blosc2.html&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This work has been made thanks to a Small Development Grant from &lt;a class="reference external" href="https://numfocus.org"&gt;NumFOCUS&lt;/a&gt;.
NumFOCUS is a non-profit organization supporting open code for better science.  If you like this, consider giving a donation to them (and if you like our work, you can nominate it to our project too!).  Thanks!&lt;/p&gt;
&lt;/section&gt;</description><category>blosc2 python user-defined filters codecs</category><guid>https://blosc.org/posts/python-blosc2-pipeline/</guid><pubDate>Thu, 15 Dec 2022 08:00:20 GMT</pubDate></item></channel></rss>