<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Projects of TMR</title>
	<atom:link href="/feed/" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>Science × Programming</description>
	<lastBuildDate>Wed, 21 Aug 2024 12:01:40 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.1</generator>
	<item>
		<title>GSOC 2024  &#8211; Bivariate Colormaps &#8211; Summary</title>
		<link>/gsoc-2024-bivariate-colormaps-summary/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Tue, 20 Aug 2024 15:40:08 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">/?p=246</guid>

					<description><![CDATA[Color mapping with multiple channels has long been a requested feature in Matplotlib, one this Google Summer of Code project (GSOC) has attempted to address, the figures below shows the desired functionality: While a complete solution for bivariate color mapping has not been merged to main at the time of writing, the intention is to... <a class="more-link" href="/gsoc-2024-bivariate-colormaps-summary/#more-246">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>Color mapping with multiple channels has long been a <a href="https://github.com/matplotlib/matplotlib/issues/14168">requested feature</a> in Matplotlib, one this Google Summer of Code project (GSOC) has attempted to address, the figures below shows the desired functionality:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full is-resized"><img fetchpriority="high" decoding="async" width="879" height="578" src="/wp-content/uploads/2024/05/image-4.png" alt="" class="wp-image-167" style="width:570px;height:auto" srcset="/wp-content/uploads/2024/05/image-4.png 879w, /wp-content/uploads/2024/05/image-4-300x197.png 300w, /wp-content/uploads/2024/05/image-4-768x505.png 768w, /wp-content/uploads/2024/05/image-4-600x395.png 600w" sizes="(max-width: 879px) 100vw, 879px" /></figure></div>


<figure class="wp-block-image size-full"><img decoding="async" width="940" height="542" src="/wp-content/uploads/2024/08/image-6.png" alt="" class="wp-image-269" srcset="/wp-content/uploads/2024/08/image-6.png 940w, /wp-content/uploads/2024/08/image-6-300x173.png 300w, /wp-content/uploads/2024/08/image-6-768x443.png 768w, /wp-content/uploads/2024/08/image-6-600x346.png 600w" sizes="(max-width: 940px) 100vw, 940px" /></figure>



<span id="more-246"></span>



<p>While a complete solution for bivariate color mapping has not been merged to main at the time of writing, the intention is to have it in place for the 3.10 release of Matplotlib (~October 2024). This represents a delay of the project as compared to the original timeline, caused by a change in the scope of the project. </p>



<p>Originally, this GSOC project was based on replacing <code>cm.ScalarMappable</code> with a vector version<code>,cm.VectorMappable</code>, (<a href="https://github.com/matplotlib/matplotlib/pull/28428">relevant PR</a>). However, with the involvement of the API lead <a href="https://github.com/timhoffm">@timhoffm</a> and project lead <a href="https://github.com/tacaswell" data-type="link" data-id="https://github.com/tacaswell">@tacasawell</a> it was decided to rework the <code>data→color</code> pipeline before implementing bivariate color mapping.</p>



<h2 class="wp-block-heading">Color by numbers (advanced version)</h2>



<p>Representing data with color lies at the root of many of the plotting methods in Matplotlib. In practice, this is handled by the class <code>cm.ScalarMappable</code> which handles normalization (converting data to a 0-1 scale through <code>colors.Normalize</code>) and color mapping (converting the 0-1 encoding to colors through <code>colors.Colormap</code>). <code>cm.ScalarMappable</code> is therefore at the root of the inheritance tree, together with `artist.Artist`. The figure below shows all the top-level plotting methods that use the data→color transformation.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="604" src="/wp-content/uploads/2024/08/rect400-1024x604.png" alt="" class="wp-image-248" srcset="/wp-content/uploads/2024/08/rect400-1024x604.png 1024w, /wp-content/uploads/2024/08/rect400-300x177.png 300w, /wp-content/uploads/2024/08/rect400-768x453.png 768w, /wp-content/uploads/2024/08/rect400-1536x905.png 1536w, /wp-content/uploads/2024/08/rect400-2048x1207.png 2048w, /wp-content/uploads/2024/08/rect400-1400x825.png 1400w, /wp-content/uploads/2024/08/rect400-600x354.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>What <a href="https://github.com/timhoffm">@timhoffm</a> <a href="https://github.com/matplotlib/matplotlib/pull/28428">argued</a> was that the <code>data→color</code> pipeline may be shared by multiple elements in the same figure, but the current class hierarchy (subclassing <code>cm.ScallarMappable</code>) is not conducive to this use case, i.e. the user must <a href="https://matplotlib.org/stable/gallery/images_contours_and_fields/multi_image.html#advanced-additionally-sync-the-colormap">manually add callbacks</a> between different graphical objects to obtain functionality such as syncing the colormaps across plots</p>



<p>Changing the class hierarchy would have major positive implications:</p>



<ol class="wp-block-list">
<li>Increased functionality when working with multiple graphical elements that share a <code>data→color</code> pipeline</li>



<li>Simplify code maintenance separating the artist (graphical element) from the <code>data→color</code> pipeline¹ (i.e. more modular code)</li>



<li>Simplify the introduction of bivariate and multivariate color mapping</li>
</ol>



<p>The proposed architecture change is as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="982" height="1024" src="/wp-content/uploads/2024/08/rect1242-1-982x1024.png" alt="" class="wp-image-252" style="width:585px;height:auto" srcset="/wp-content/uploads/2024/08/rect1242-1-982x1024.png 982w, /wp-content/uploads/2024/08/rect1242-1-288x300.png 288w, /wp-content/uploads/2024/08/rect1242-1-768x801.png 768w, /wp-content/uploads/2024/08/rect1242-1-600x626.png 600w, /wp-content/uploads/2024/08/rect1242-1.png 995w" sizes="(max-width: 982px) 100vw, 982px" /></figure></div>


<p>Where the <code>data→color</code> transformation is contained in the new <code>colorizer.Colorizer</code> module, and one colorizer can control the <code>data→color</code> pipeline for multiple plots:<br></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="478" src="/wp-content/uploads/2024/08/rect1246-1024x478.png" alt="" class="wp-image-255" srcset="/wp-content/uploads/2024/08/rect1246-1024x478.png 1024w, /wp-content/uploads/2024/08/rect1246-300x140.png 300w, /wp-content/uploads/2024/08/rect1246-768x359.png 768w, /wp-content/uploads/2024/08/rect1246-600x280.png 600w, /wp-content/uploads/2024/08/rect1246.png 1154w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>These changes are implemented in the <a href="https://github.com/matplotlib/matplotlib/pull/28658#top">New data → color pipeline</a> PR, and the (slightly outdated) video shows how multiple images that share a colorizer behave.</p>



<figure class="wp-block-video"><video controls src="/wp-content/uploads/2024/08/1.mp4"></video></figure>



<h2 class="wp-block-heading">Colormap classes for bivariate (and multivariate) colormaps</h2>



<p>A separate PR, <a href="https://github.com/matplotlib/matplotlib/pull/28454#top">MultivarColormap and BivarColormap</a>, introduces classes that hold bivariate and multivariate colormaps.</p>



<p>The bivariate colormaps come in different shapes:<br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="990" height="374" src="/wp-content/uploads/2024/08/image.png" alt="" class="wp-image-253" srcset="/wp-content/uploads/2024/08/image.png 990w, /wp-content/uploads/2024/08/image-300x113.png 300w, /wp-content/uploads/2024/08/image-768x290.png 768w, /wp-content/uploads/2024/08/image-600x227.png 600w" sizes="(max-width: 990px) 100vw, 990px" /></figure>



<p>while the multivariate colormaps come with different combination modes:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="995" height="374" src="/wp-content/uploads/2024/08/image-1.png" alt="" class="wp-image-254" srcset="/wp-content/uploads/2024/08/image-1.png 995w, /wp-content/uploads/2024/08/image-1-300x113.png 300w, /wp-content/uploads/2024/08/image-1-768x289.png 768w, /wp-content/uploads/2024/08/image-1-600x226.png 600w" sizes="(max-width: 995px) 100vw, 995px" /></figure>



<h2 class="wp-block-heading">Exposing bivariate (and multivariate) colormaps to plotting methods</h2>



<p>Using bivariate or multivariate colormaps requires a multivariate normalization, so before the new colormaps can be exposed to users, <a href="https://github.com/matplotlib/matplotlib/commit/1fb5dd808b1367aaad66da29ebeab3b722f6aa5e">a <code>colors.MultiNorm</code> class</a> must be implemented. By using a <code>colors.MultiNorm</code> and a <code>colors.BivarColormap</code> with a  <code>colorizer.Colorizer</code> object, the new functionality can <a href="https://github.com/matplotlib/matplotlib/commit/6018bd7a79518e5058f59431fd036979c16ecfd6">be exposed to the top level plotting functions</a> (i.e. <code>ax.imshow()</code>). </p>



<p>Both of these features exist as commits (links above), and will be part of a PR after the <a href="https://github.com/matplotlib/matplotlib/pull/28658" data-type="link" data-id="https://github.com/matplotlib/matplotlib/pull/28658">two</a> <a href="https://github.com/matplotlib/matplotlib/pull/28454">other</a> PRs are merged. </p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="574" src="/wp-content/uploads/2024/08/g1370-1024x574.png" alt="" class="wp-image-257" srcset="/wp-content/uploads/2024/08/g1370-1024x574.png 1024w, /wp-content/uploads/2024/08/g1370-300x168.png 300w, /wp-content/uploads/2024/08/g1370-768x430.png 768w, /wp-content/uploads/2024/08/g1370-600x336.png 600w, /wp-content/uploads/2024/08/g1370.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Combined with the functionality of the <code>colorizer.Colorizer</code>, this solution allows for coupled plots where a single axes from a bivariate or multivariate colormap can be used together with the full colormap:</p>



<figure class="wp-block-video"><video controls src="/wp-content/uploads/2024/08/2.mp4"></video></figure>



<h2 class="wp-block-heading">Choosing bivariate and multivariate colormaps</h2>



<p>Once the functionality is implemented, there needs to be colormaps for the user to choose from. I have written two blogposts describing the design considerations relevant for their design:</p>



<p><a href="/designing-2d-colormaps/" data-type="post" data-id="107">Designing 2D colormaps</a></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="750" src="/wp-content/uploads/2024/08/image-2.png" alt="" class="wp-image-260" srcset="/wp-content/uploads/2024/08/image-2.png 1024w, /wp-content/uploads/2024/08/image-2-300x220.png 300w, /wp-content/uploads/2024/08/image-2-768x563.png 768w, /wp-content/uploads/2024/08/image-2-600x439.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><a href="/multivariate-colormaps-for-n-dimensions/" data-type="post" data-id="159">Multivariate colormaps for n dimensions</a></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="549" src="/wp-content/uploads/2024/08/image-3.png" alt="" class="wp-image-261" srcset="/wp-content/uploads/2024/08/image-3.png 1024w, /wp-content/uploads/2024/08/image-3-300x161.png 300w, /wp-content/uploads/2024/08/image-3-768x412.png 768w, /wp-content/uploads/2024/08/image-3-600x322.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="549" src="/wp-content/uploads/2024/08/image-4.png" alt="" class="wp-image-262" srcset="/wp-content/uploads/2024/08/image-4.png 1024w, /wp-content/uploads/2024/08/image-4-300x161.png 300w, /wp-content/uploads/2024/08/image-4-768x412.png 768w, /wp-content/uploads/2024/08/image-4-600x322.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>In an effort to separate different concerns, these have not been a part of PR#28454 <a href="https://github.com/matplotlib/matplotlib/pull/28454#top">MultivarColormap and BivarColormap</a>, and will need to be introduced in a separate PR.</p>



<p>This should also feature a guide to how the <a href="http://trygvrad.com/mpl_docs/Bivariate colormaps.html">new functionality</a> <a href="http://trygvrad.com/mpl_docs/Multivariate colormaps.html">can be used</a>, and <a href="http://trygvrad.com/mpl_docs/Bivariate colormap reference.html">how to choose</a> <a href="http://trygvrad.com/mpl_docs/Multivariate colormap reference.html">a colormap</a>.</p>



<h2 class="wp-block-heading">Colorbars</h2>



<p>To complete the figure, a multidimensional colorbar must be added. There is a draft for this <a href="https://github.com/matplotlib/matplotlib/commit/a64bb195e37d0a4a2169b7d24beeec3bd59aa3ad">here</a>, but some work still remains. Firstly, a <code>BivarColorbar</code> class must be created (in the multivariate case &#8211; a list of <code>Colorbar</code> objects will work). Secondly, the automatic positioning of the colorbars in the figure needs some improvement. </p>



<h2 class="wp-block-heading">Summary</h2>



<p>This project has grown in scope and now includes:</p>



<ol class="wp-block-list">
<li>Classes for bivariate and multivariate colormaps <a href="https://github.com/matplotlib/matplotlib/pull/28454">#28454</a></li>



<li>Reworking the <code>data→color</code> pipeline<a href="https://github.com/matplotlib/matplotlib/pull/28658#top">#28658</a> </li>



<li>Exposing bivariate and multivariate colormaps to the plotting methods</li>



<li>A selection of bivariate and multivariate colormaps</li>



<li>&#8220;Colorbar&#8221; objects for the bivariate and multivariate case</li>
</ol>



<p>Of these, step 1 has been approved, and step 2 is (hopefully) near the end of code review. Step 3 waits for step 1 and 2 to be merged, but the PR will be made soon. Step 4 is similarly ready, but the PR should be made after the code review of step 3 has started. The PR for step 5 will similarly follow once step 4 is merged.</p>



<p>In other words, there is still some work to be done, but as there exists drafts for all the remaining functionality the remaining workloads are less code-intensive, and instead lean towards the collaborative aspects of open source. This project involves re-organizing the <code>data→color</code> pipeline for all relevant plotting methods, and it is therefore important that all the Matplotlib maintainers get to chime in at every step of the way. This process takes time, but ensures a better outcome. </p>



<p>Thank you to Hannah and Kyle for guiding me through GSOC, and to Elliot, Tom, Tim and Jody for feedback on the PRs.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>¹ The distinction between the <code>Colorizer</code> and <code>ColorizingArtist</code> objects are as follows:</p>



<ul class="wp-block-list">
<li><code>ColorizingArtist</code> subclasses <code>Artist</code>, i.e. it refers to a specific graphical element in a plot. It has the following atributes:
<ul class="wp-block-list">
<li>data</li>



<li>alpha (opacity)</li>



<li><code>colorizer</code></li>



<li>A <code>.draw(renderer)</code> method to render (display) the graphical elements. </li>



<li>&#8230;²</li>



<li>&#8230;³</li>
</ul>
</li>



<li><code>Colorizer</code> holds the <code>data→color</code> pipeline. It consists of:
<ul class="wp-block-list">
<li><code>norm</code></li>



<li><code>cmap</code></li>



<li>A <code>.to_rgba(data, alpha)</code> method that assignes color (red, green, blue, alpha) to data using the <code>norm</code> and <code>cmap</code>.</li>
</ul>
</li>
</ul>



<p>When rendering a <code>ColorizingArtist</code>, it will invoke <code>self.colorizer.to_rgba(data, alpha)</code> to determine what colors to render.</p>



<p>² <code>ColorizingArtist</code> also has attributes required of an <code>Artist</code>, such as position, size, etc. relating to rendering of the artist in an <code>Axes</code>. </p>



<p>³ The <code>norm</code> and <code>cmap</code> are accessible as attributes on a <code>ColorizingArtist</code> through the use of <code>@property</code>. For many users, the distinction between the <code>ColorizingArtist</code> and <code>Colorizer</code> will therefore not be felt. </p>



<p></p>
]]></content:encoded>
					
		
		<enclosure url="/wp-content/uploads/2024/08/1.mp4" length="561098" type="video/mp4" />
<enclosure url="/wp-content/uploads/2024/08/2.mp4" length="355100" type="video/mp4" />

			</item>
		<item>
		<title>Multivariate colormaps for n dimensions</title>
		<link>/multivariate-colormaps-for-n-dimensions/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Wed, 24 Jul 2024 10:49:32 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">/?p=159</guid>

					<description><![CDATA[Much work has been done in the last decade related to 1-dimensional colormaps (see for eaxmple Peter Kovesis paper and Nathaniel Smith and Stéfan van der Walts talk (2015)). This post follows my previous post on 2D colormaps, and many of the design principles will be the same. However, with n ≥ 3, it quickly... <a class="more-link" href="/multivariate-colormaps-for-n-dimensions/#more-159">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>Much work has been done in the last decade related to 1-dimensional colormaps (see for eaxmple <a href="https://arxiv.org/search/cs?searchtype=author&amp;query=Kovesi,+P">Peter Kovesis paper</a> and <a href="https://www.youtube.com/watch?v=xAoljeRJ3lU">Nathaniel Smith and Stéfan van der Walts talk</a> (2015)).</p>



<p>This post follows my previous post on<a href="../designing-2d-colormaps"> 2D colormaps</a>, and many of the design principles will be the same. However, with n ≥ 3, it quickly becomes unfeasible to create a full lookup table. With n = 3 channels and 256 values in each channel, the lookup table would be a matrix of 256^3 elements, likely much larger than the image the colormap is applied to. Instead, n independent 1D lookup tables are created, and the resulting colors are then combined.</p>



<span id="more-159"></span>



<p>The simplest approach is to assign one color channel (R, G, B) to each channel in the image; in the below image the same data is shown mapped to (R, G, B) and (G, B, R): <a href="ttps://arxiv.org/abs/1812.10366" data-type="link" data-id="ttps://arxiv.org/abs/1812.10366">[data source]</a></p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="718" height="303" src="/wp-content/uploads/2024/07/image.png" alt="" class="wp-image-180" srcset="/wp-content/uploads/2024/07/image.png 718w, /wp-content/uploads/2024/07/image-300x127.png 300w, /wp-content/uploads/2024/07/image-600x253.png 600w" sizes="(max-width: 718px) 100vw, 718px" /></figure></div>


<p>Both images above display the same data, with the same normalization, but by swapping the color different aspects are highlighted. The green channel has higher perceptual lightness and saturation, and the features of the green channel are therefore highlighted. <br>The lightness and saturation of the R, G and B channels can be visualized by displaying the three channels in a perceptually uniform colorspace. </p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_multivar_colormaps/RGB_multivar.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>



<p>We can define the following figures of merit for multivariate colormaps:</p>



<ol class="wp-block-list">
<li>Each channel should be perceptually uniform.</li>



<li>All channels should have similar lightness and saturation.</li>



<li>The perceptual distance between the channels should be maximized.</li>
</ol>



<p>Since there are perceptually uniform colorspaces available in literature (for example CAM02-LCD <a href="https://doi.org/10.1002/col.20227" data-type="link" data-id="https://doi.org/10.1002/col.20227">Luo et al. (2006)</a>), we can use these to design colormaps that match the figures of merit above:</p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_multivar_colormaps/new_RGB_multivar.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="718" height="303" src="/wp-content/uploads/2024/07/image-1.png" alt="" class="wp-image-181" srcset="/wp-content/uploads/2024/07/image-1.png 718w, /wp-content/uploads/2024/07/image-1-300x127.png 300w, /wp-content/uploads/2024/07/image-1-600x253.png 600w" sizes="(max-width: 718px) 100vw, 718px" /></figure></div>


<p>The above figure show the same data again but this time visualized with the newly generated colormaps. Again, they are shown with two different assignments to the three color channels. There are still perceptual differences, but there should be no features in the left image that are not visible in the right image. (Note that they will appear different to those that are colorblind, or if printed in black and white &#8211; more on this later.)</p>



<h2 class="wp-block-heading">Combining colors</h2>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="696" height="252" src="/wp-content/uploads/2024/07/image-38.png" alt="" class="wp-image-241" style="width:838px;height:auto" srcset="/wp-content/uploads/2024/07/image-38.png 696w, /wp-content/uploads/2024/07/image-38-300x109.png 300w, /wp-content/uploads/2024/07/image-38-600x217.png 600w" sizes="(max-width: 696px) 100vw, 696px" /></figure>



<p>When the three channels are combined, they may produce colors that do not exist in any of the individual channels. In the above example, the colors are added, but there are a other possible <a href="https://www.clipstudio.net/how-to-draw/archives/154182">blending modes</a>:</p>



<ul class="wp-block-list">
<li><strong>Additive: R = R₁ + R₂ + R₃             ← example above</strong></li>



<li>Lighten:  R = max(R₁, R₂, R₃)</li>



<li>Darken:   R = min(R₁, R₂, R₃)</li>



<li>Multiply: R = R₁ * R₂ * R₃</li>



<li>Screen:   R = (R₁⁻¹ * R₂⁻¹ * R₃⁻¹)⁻¹</li>



<li>&#8230;</li>
</ul>



<p>as well as more exotic ways to work within colorspace:</p>



<ul class="wp-block-list">
<li>Mixing in perceptual uniform space</li>



<li>Non-linear mixing, e.g.  R = √(R₁² + R₂² + R₃²)</li>



<li>Choosing the colorbar corresponding to the highest input value, ignoring the others </li>



<li>&#8230;</li>
</ul>



<p>In terms of perceptual uniformity, it would be best to combine the colors in perceptually uniform colorspace, but there are pragmatic reasons to instead combine colors sRGB:</p>



<ul class="wp-block-list">
<li>The conversion sRGB ←→ CAM02-LCD is numerically costly</li>



<li>The conversion sRGB ←→ CAM02-LCD is numerically unstable</li>



<li>Combining colors in sRGB space gives acceptable results </li>
</ul>



<p>It is therefore reasonable to design colormaps in a perceptually uniform space, but combine them in sRGB. However, we must be carefull to check that that this does not produce any unwanted artifacts when the colormaps are combined. This is most acute for n = 3 channels, where it is possible to create colormaps such that each color in the output image stems from a unique combination of input.</p>



<h2 class="wp-block-heading">Designing colormaps with n = 3 channels</h2>



<p>With n=3 channels, we can think of colorspace as a cube, with each channel representing one of the axes. If we are working with dense data, such that the input spans the full 3D volume of the cube, then we can add the following two figures of merit:</p>



<ol start="4" class="wp-block-list">
<li class="ol {counter-reset: start 9}">Equal mixing of the components provides a grayscale</li>



<li>No combination leaves sRGB space</li>
</ol>



<p>We can visualize the colormaps above as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="262" height="263" src="/wp-content/uploads/2024/07/image-3-edited.png" alt="" class="wp-image-187" style="width:259px;height:auto" srcset="/wp-content/uploads/2024/07/image-3-edited.png 262w, /wp-content/uploads/2024/07/image-3-edited-150x150.png 150w" sizes="(max-width: 262px) 100vw, 262px" /></figure></div>


<p>By visual inspection we can see that this is relatively uniform around the origin (black), but non-uniform around the other extreme (white), where it wants to leave the sRGB colorspace. The same can be seen in 3D, where it also becomes apparent that the &#8220;greyscale&#8221; has a weak tint.</p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_multivar_colormaps/new_RGB_multivar2.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>



<p>We can adjust the colormap by reducing the maximum saturation and lightness of each channel, this to make sure the entire colormap fits in sRGB space. In the figure below the origin is fixed at (0, 0, 0) in sRGB space, and the maximum at (1, 1, 1). The grayscale has also been adjusted by minor shifts to the individual channels, so that the greyscale follows the sRGB greyscale.</p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_multivar_colormaps/new_RGB_multivar3.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="563" height="273" src="/wp-content/uploads/2024/07/image-4.png" alt="" class="wp-image-188" srcset="/wp-content/uploads/2024/07/image-4.png 563w, /wp-content/uploads/2024/07/image-4-300x145.png 300w" sizes="(max-width: 563px) 100vw, 563px" /></figure></div>


<p>This colormap fulfills all the figures of merit listed above, and will work for any 3-channel dataset. This particular example has sparse data (most pixels are only non-zero in one channel) and the resulting image therefore appears a little drab.</p>



<h2 class="wp-block-heading">Colorblindness</h2>



<p>Around 5-10% of the male population has some form of red-green colorblindness. For many of them, the colormap above looks as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="554" height="273" src="/wp-content/uploads/2024/07/image-5.png" alt="" class="wp-image-189" srcset="/wp-content/uploads/2024/07/image-5.png 554w, /wp-content/uploads/2024/07/image-5-300x148.png 300w" sizes="(max-width: 554px) 100vw, 554px" /></figure></div>


<p>Note how the green and red channels appear almost identical. This is not an accident, but the result of a choice made when making the colormaps. The blue channel follows <em>negative b</em> (blue) closely in <em>L*a*b*</em> colorspace, and as a result the green and red channels have a very similar value of <em>positive b</em> (yellow). (red-green colorblindness is in practice the abscence of the <em>a</em> axis in <em>L*a*b*</em> colorspace).</p>



<p>It is impossible to accommodate colorblindness completely in a 3-channel image. However, the choice made above was <em>particularly</em> bad. A better choice is to fix one of the channels to be along the <em>a</em> axis. The resulting colormap will then also have three distinct channels for those that are colorblind (grey, yellow, blue). </p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="554" height="273" src="/wp-content/uploads/2024/07/image-8.png" alt="" class="wp-image-192" srcset="/wp-content/uploads/2024/07/image-8.png 554w, /wp-content/uploads/2024/07/image-8-300x148.png 300w" sizes="(max-width: 554px) 100vw, 554px" /></figure></div>


<p>This is by no means perfect, but it is clearly a better choice. <br>For those of use who are not colorblind, the colormap above looks as follows:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="554" height="273" src="/wp-content/uploads/2024/07/image-9.png" alt="" class="wp-image-193" srcset="/wp-content/uploads/2024/07/image-9.png 554w, /wp-content/uploads/2024/07/image-9-300x148.png 300w" sizes="(max-width: 554px) 100vw, 554px" /></figure></div>


<iframe loading="lazy" src="https://trygvrad.github.io/plotly_multivar_colormaps/new_RGB_multivar4.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>



<h2 class="wp-block-heading">The problem with black</h2>



<p>When discretized to 12 steps, the colormaps look like this:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="717" height="75" src="/wp-content/uploads/2024/07/image-10.png" alt="" class="wp-image-195" style="width:839px;height:auto" srcset="/wp-content/uploads/2024/07/image-10.png 717w, /wp-content/uploads/2024/07/image-10-300x31.png 300w, /wp-content/uploads/2024/07/image-10-600x63.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure>



<p>On the right side, the individual segments can be easily discerned, but this is not true on the left, where several segments are effectively the same shade of black. I believe the issue is related to how most monitors cannot truly represent black in its purest form, and this is difficult to account for in the transformation form a perceptually linear colorspace to sRGB. </p>



<p>We can slighly shift the colors in the colormap in order to improve the perceptual fidelity in the darkest parts of the colormap. </p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="717" height="125" src="/wp-content/uploads/2024/07/image-12.png" alt="" class="wp-image-197" style="width:839px;height:auto" srcset="/wp-content/uploads/2024/07/image-12.png 717w, /wp-content/uploads/2024/07/image-12-300x52.png 300w, /wp-content/uploads/2024/07/image-12-600x105.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure>



<p>The approach used here is similar to a gamma correction, but acts independently on the lightness and the saturation. There is also a non-linearity applied when the origin is fixed to (0,0,0) in sRGB. The changes are best visualized by plotting the lightness, saturation and hue of each component:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="889" height="299" src="/wp-content/uploads/2024/07/image-15.png" alt="" class="wp-image-200" style="width:840px;height:auto" srcset="/wp-content/uploads/2024/07/image-15.png 889w, /wp-content/uploads/2024/07/image-15-300x101.png 300w, /wp-content/uploads/2024/07/image-15-768x258.png 768w, /wp-content/uploads/2024/07/image-15-600x202.png 600w" sizes="(max-width: 889px) 100vw, 889px" /></figure>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="889" height="299" src="/wp-content/uploads/2024/07/image-16.png" alt="" class="wp-image-201" style="width:840px;height:auto" srcset="/wp-content/uploads/2024/07/image-16.png 889w, /wp-content/uploads/2024/07/image-16-300x101.png 300w, /wp-content/uploads/2024/07/image-16-768x258.png 768w, /wp-content/uploads/2024/07/image-16-600x202.png 600w" sizes="(max-width: 889px) 100vw, 889px" /></figure>



<p>It is worth noting that this is not an issue in the bright part of colorspace, and colormaps with white at the origin perform better with regard to perceptual uniformity:<br>(I refer to these as subtractive colormaps, in contrast to the additive colormaps discussed so far.)</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="554" height="273" src="/wp-content/uploads/2024/07/image-23.png" alt="" class="wp-image-209" srcset="/wp-content/uploads/2024/07/image-23.png 554w, /wp-content/uploads/2024/07/image-23-300x148.png 300w" sizes="(max-width: 554px) 100vw, 554px" /></figure></div>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="717" height="57" src="/wp-content/uploads/2024/07/image-17.png" alt="" class="wp-image-202" style="width:838px;height:auto" srcset="/wp-content/uploads/2024/07/image-17.png 717w, /wp-content/uploads/2024/07/image-17-300x24.png 300w, /wp-content/uploads/2024/07/image-17-600x48.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="888" height="274" src="/wp-content/uploads/2024/07/image-20.png" alt="" class="wp-image-206" srcset="/wp-content/uploads/2024/07/image-20.png 888w, /wp-content/uploads/2024/07/image-20-300x93.png 300w, /wp-content/uploads/2024/07/image-20-768x237.png 768w, /wp-content/uploads/2024/07/image-20-600x185.png 600w" sizes="(max-width: 888px) 100vw, 888px" /></figure>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="571" height="273" src="/wp-content/uploads/2024/07/image-21.png" alt="" class="wp-image-207" srcset="/wp-content/uploads/2024/07/image-21.png 571w, /wp-content/uploads/2024/07/image-21-300x143.png 300w" sizes="(max-width: 571px) 100vw, 571px" /></figure></div>


<h2 class="wp-block-heading"> Colormaps with n ≥  4 channels</h2>



<p>For n ≥ 4, different each color in colorspace is no longer unique (there may be different ways to produce some of the same color in the image) and are therefore best applied only to sparse data (most pixels are only non-zero in one channel so that each pixel in the resulting image has a color close to one of the colorbars). This means that less attention should be paid to how the colormaps combine, as the combined colors are expected to rarely appear in the intended use case. We therefore use only the three first figures of merit:</p>



<ol class="wp-block-list">
<li>Each channel should be perceptually uniform.</li>



<li>All channels should have similar lightness and saturation.</li>



<li>The perceptual distance between the channels should be maximized.</li>



<li><s>Equal mixing of the components provides a grayscale</s></li>



<li><s>No combination leaves sRGB space</s></li>
</ol>



<p>Keeping all the other points discussed above in mind (adjusting dark colors, rotating the hues to maximize difference in color for those that are colorblind) we can produce colormaps with 4-8 colors:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="717" height="58" src="/wp-content/uploads/2024/07/image-29.png" alt="" class="wp-image-216" srcset="/wp-content/uploads/2024/07/image-29.png 717w, /wp-content/uploads/2024/07/image-29-300x24.png 300w, /wp-content/uploads/2024/07/image-29-600x49.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="717" height="58" src="/wp-content/uploads/2024/07/image-33.png" alt="" class="wp-image-220" srcset="/wp-content/uploads/2024/07/image-33.png 717w, /wp-content/uploads/2024/07/image-33-300x24.png 300w, /wp-content/uploads/2024/07/image-33-600x49.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="717" height="58" src="/wp-content/uploads/2024/07/image-30.png" alt="" class="wp-image-217" srcset="/wp-content/uploads/2024/07/image-30.png 717w, /wp-content/uploads/2024/07/image-30-300x24.png 300w, /wp-content/uploads/2024/07/image-30-600x49.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="717" height="58" src="/wp-content/uploads/2024/07/image-31.png" alt="" class="wp-image-218" srcset="/wp-content/uploads/2024/07/image-31.png 717w, /wp-content/uploads/2024/07/image-31-300x24.png 300w, /wp-content/uploads/2024/07/image-31-600x49.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="717" height="58" src="/wp-content/uploads/2024/07/image-32.png" alt="" class="wp-image-219" srcset="/wp-content/uploads/2024/07/image-32.png 717w, /wp-content/uploads/2024/07/image-32-300x24.png 300w, /wp-content/uploads/2024/07/image-32-600x49.png 600w" sizes="(max-width: 717px) 100vw, 717px" /></figure></div>


<p>We can evaluate these as we did with the n=3 colormaps, here for 4 and 8 colors.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="889" height="290" src="/wp-content/uploads/2024/07/image-35.png" alt="" class="wp-image-222" srcset="/wp-content/uploads/2024/07/image-35.png 889w, /wp-content/uploads/2024/07/image-35-300x98.png 300w, /wp-content/uploads/2024/07/image-35-768x251.png 768w, /wp-content/uploads/2024/07/image-35-600x196.png 600w" sizes="(max-width: 889px) 100vw, 889px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="889" height="290" src="/wp-content/uploads/2024/07/image-34.png" alt="" class="wp-image-221" srcset="/wp-content/uploads/2024/07/image-34.png 889w, /wp-content/uploads/2024/07/image-34-300x98.png 300w, /wp-content/uploads/2024/07/image-34-768x251.png 768w, /wp-content/uploads/2024/07/image-34-600x196.png 600w" sizes="(max-width: 889px) 100vw, 889px" /></figure>



<p>We see that in both lightness and saturation there are some inconsistencies between different colors. This is because the colormaps approach the limits of sRGB space. The inconsistencies could have been avoided by reducing the maximum lightness and saturation, which would in turn results in more dull colormaps. These colormaps are the result of attempting to strike a balance between vibrant colormaps and perceptual uniformity.</p>



<p>(For data that is <em>not</em> sparse a different combination mode may be used, but that topic goes beyond the scope of this post.)</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<h2 class="wp-block-heading">A set of colormaps with no bad choices</h2>



<p>Combining all these together produces the following colormaps:</p>
</blockquote>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="549" src="/wp-content/uploads/2024/07/image-36-1024x549.png" alt="" class="wp-image-223" srcset="/wp-content/uploads/2024/07/image-36-1024x549.png 1024w, /wp-content/uploads/2024/07/image-36-300x161.png 300w, /wp-content/uploads/2024/07/image-36-768x412.png 768w, /wp-content/uploads/2024/07/image-36-1536x824.png 1536w, /wp-content/uploads/2024/07/image-36-2048x1098.png 2048w, /wp-content/uploads/2024/07/image-36-1400x751.png 1400w, /wp-content/uploads/2024/07/image-36-600x322.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure></div>


<ul class="wp-block-list">
<li>Two (A, B) variants for n = 2 with different hues
<ul class="wp-block-list">
<li>Both variants <strong>stay within sRGB</strong> and end at white when combined at the maximum</li>
</ul>
</li>



<li>Four colormaps for n = 3
<ul class="wp-block-list">
<li>A and B are designed so that all combinations <strong>stay within sRGB</strong> space
<ul class="wp-block-list">
<li>A and B have different hues</li>
</ul>
</li>



<li>C and D are designed to <strong>exceed sRGB space</strong>
<ul class="wp-block-list">
<li>C has the same hue as A and D has the same hue as B</li>
</ul>
</li>
</ul>
</li>



<li>Two colormaps for n = 4 with different hues
<ul class="wp-block-list">
<li>Both  alternatives <strong>exceed sRGB space</strong></li>
</ul>
</li>



<li>One alternative for each of n = 5, 6, 7 and 8
<ul class="wp-block-list">
<li>At n=8 is the result of combining the two sets at n = 4</li>
</ul>
</li>
</ul>



<p>As noted previously, the <em>subtractive</em> colormaps are more perceptually uniform:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="549" src="/wp-content/uploads/2024/07/image-37-1024x549.png" alt="" class="wp-image-224" srcset="/wp-content/uploads/2024/07/image-37-1024x549.png 1024w, /wp-content/uploads/2024/07/image-37-300x161.png 300w, /wp-content/uploads/2024/07/image-37-768x412.png 768w, /wp-content/uploads/2024/07/image-37-1536x824.png 1536w, /wp-content/uploads/2024/07/image-37-2048x1098.png 2048w, /wp-content/uploads/2024/07/image-37-1400x751.png 1400w, /wp-content/uploads/2024/07/image-37-600x322.png 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>By conforming to the figures of merit, we ensure that the most common pitfalls when choosing a colormap can be avoided. <strong>I.e. these colormaps will never be a bad choice</strong>. As such, they form a suitable basis set for general-purpose plotting software, such as matplotlib, pyplot, pyqtgraph, and others.</p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Google SoC &#8211; Bivariate colormaps</title>
		<link>/google-soc-bivariate-colormaps/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Sat, 25 May 2024 13:26:51 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">/?p=162</guid>

					<description><![CDATA[I have been accepted to Google Summer of Code!Over the summer I will be working for Numfocus on Bivariate colormaps for matplotlib a feature that has been requested for a long time. If you are not familiar with bivariate color maps, they come in handy to visualize complex numbers, here exemplified by the Mandelbrot set:... <a class="more-link" href="/google-soc-bivariate-colormaps/#more-162">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>I have been <a href="https://summerofcode.withgoogle.com/programs/2024/projects/oEBt7vSe" data-type="link" data-id="https://summerofcode.withgoogle.com/programs/2024/projects/oEBt7vSe">accepted to Google Summer of Code</a>!<br>Over the summer I will be working for Numfocus on Bivariate colormaps for matplotlib a <a href="https://github.com/matplotlib/matplotlib/issues/14168">feature that has been requested for a long time</a>.</p>



<p><br>If you are not familiar with bivariate color maps, they come in handy to visualize complex numbers, here exemplified by the <a href="https://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot</a> set:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="879" height="578" src="/wp-content/uploads/2024/05/image-4.png" alt="" class="wp-image-167" style="width:588px;height:auto" srcset="/wp-content/uploads/2024/05/image-4.png 879w, /wp-content/uploads/2024/05/image-4-300x197.png 300w, /wp-content/uploads/2024/05/image-4-768x505.png 768w, /wp-content/uploads/2024/05/image-4-600x395.png 600w" sizes="(max-width: 879px) 100vw, 879px" /></figure>



<span id="more-162"></span>



<p>But bivariate colormaps are not limited to the number plane, and have a number of uses in different disciplines. Where the above graph shows correlated data (complex numbers), the figure below uses a bivariate colormap to visualize two independent datasets, GDP per capita, and growth (<a href="https://www.bea.gov/data/gdp/gdp-state">data source</a>):</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="640" height="350" src="/wp-content/uploads/2024/05/image-1.png" alt="" class="wp-image-164" srcset="/wp-content/uploads/2024/05/image-1.png 640w, /wp-content/uploads/2024/05/image-1-300x164.png 300w, /wp-content/uploads/2024/05/image-1-600x328.png 600w" sizes="(max-width: 640px) 100vw, 640px" /></figure>



<p>Implementing bivariate colormaps in matplotlib requires adding functionality deep in the class hierarchy. By making these changes, it will be easy to also implement multivariate colormaps for visualizing more than two images/datasets together, as with the cells shown below (<a href="https://zenodo.org/record/3246903">data source</a>):</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="491" height="342" src="/wp-content/uploads/2024/05/image.png" alt="" class="wp-image-163" srcset="/wp-content/uploads/2024/05/image.png 491w, /wp-content/uploads/2024/05/image-300x209.png 300w" sizes="(max-width: 491px) 100vw, 491px" /></figure>



<p>There is a lot of work to be done, both on the implementation side of things, and for designing suitable colormaps. Check out the development <a href="https://github.com/trygvrad/matplotlib/tree/multivariate_colormaps_take2">branch</a> if you want to compare the changes yourself!</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="676" height="649" src="/wp-content/uploads/2024/05/image-2.png" alt="" class="wp-image-165" srcset="/wp-content/uploads/2024/05/image-2.png 676w, /wp-content/uploads/2024/05/image-2-300x288.png 300w, /wp-content/uploads/2024/05/image-2-600x576.png 600w" sizes="(max-width: 676px) 100vw, 676px" /></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Designing 2D colormaps</title>
		<link>/designing-2d-colormaps/</link>
					<comments>/designing-2d-colormaps/#respond</comments>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Mon, 12 Feb 2024 12:28:19 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">/?p=107</guid>

					<description><![CDATA[Two dimensional colormaps are seeing use in a number of domains that rely upon python ecosystem. I work with dark-field X-ray microscopy, which is one such domain, but there are numerous others, such as astronomy, polarization microscopy etc. While each domain may have dedicated tooling, darfix, astropy, xrayutilities etc, this tooling is built upon the... <a class="more-link" href="/designing-2d-colormaps/#more-107">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>Two dimensional colormaps are seeing use in a number of domains that rely upon python ecosystem. I work with dark-field X-ray microscopy, which is one such domain, but there are numerous others, such as astronomy, polarization microscopy etc. While each domain may have dedicated tooling, <a href="https://gitlab.esrf.fr/XRD/darfix">darfix</a>, <a href="https://www.astropy.org/">astropy</a>, <a href="https://xrayutilities.sourceforge.io/">xrayutilities</a> etc, this tooling is built upon the scientific python stack, including <a href="https://numpy.org/">numpy</a>, <a href="https://scipy.org/">scipy</a>, <a href="https://pandas.pydata.org/">pandas</a>, <a href="https://matplotlib.org/">matplotlib</a>, <a href="https://plotly.com/">plotly</a>, etc. Since these domains share common challenges regarding multivariate colormaps, it would be ideal to handle these challenges upstream (i.e. in matplotlib, plotly, etc.) rather than in the domain-specific packages. In addition to the technical implementation, the design criteria for multivariate colormaps must be explored.</p>



<span id="more-107"></span>



<p>For the design of colormaps, <a href="https://arxiv.org/search/cs?searchtype=author&amp;query=Kovesi,+P">Peter Kovesis</a> paper (2015) covers the topic with great diligence, as is <a href="https://www.youtube.com/watch?v=xAoljeRJ3lU">Nathaniel Smith and Stéfan van der Walts talk</a> (2015) explaining the origin of the now-ubiquitous (at least in academia) &#8216;viridis&#8217; colormap. They outline an approach centered on human perception, where colormaps are designed to be perceptually uniform while maximizing the number of levels that can be distinguished. For multivariate colormaps, this approach has not previously been implemented.</p>



<p><strong>2D colormaps</strong><br>Just as any continuous 1D colormap is a curve in colorspace, any continuous 2D colormap is a surface. <a href="https://arxiv.org/search/cs?searchtype=author&amp;query=Kovesi,+P">Peter Kovesis</a>, <a href="https://www.youtube.com/watch?v=xAoljeRJ3lU">Nathaniel Smith and Stéfan van der Walts </a>made the point that one should use a perceptually uniform colormap, such as <a href="https://en.wikipedia.org/wiki/CIELAB_color_space">CIELAB</a> when designing 1D colormaps, and this remains true for 2D colormaps. The sRBG color gamut is illustrated below in the colorspace CAM02-LCD (Large Color Difference) <a href="https://doi.org/10.1002/col.20227" data-type="link" data-id="https://doi.org/10.1002/col.20227">Luo et al. (2006)</a>.</p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/fig.html" height="520" width="420" frameborder="0" allow="fullscreen"></iframe>



<p>CAM02-LCD (and CIELAB) describes colorspace using three orthogonal axes: <strong>L*</strong>: lightness, <strong>a*</strong>: green-red, and <strong>b*</strong>: blue-yellow. The colorspace is designed to be perceptually uniform, i.e. the distance you need to move along an axis before you notice a change, should be the same along <strong>L*</strong>, <strong>a*</strong>, and <strong>b*</strong>.</p>



<p>Colormap design is further determined by the kind of data the colormap is designed to represent. <a href="https://matplotlib.org/stable/users/explain/colors/colormaps.html#ibm">Matplotlib</a> groups colormaps as <strong>sequential</strong>, <strong>diverging</strong>, and <strong>cyclic</strong> (in addition to qualitative), designed to represent data that fit into those categories. For 2D colormaps, the two axis can represent categorically different data, making 6 different categories: sequential×sequential, sequential×diverging, sequential×cyclic, diverging×diverging, diverging×cyclic, cyclic×cyclic. One can compare the different categories to the axis in CIELAB space, where <strong>L*</strong> is <strong>sequential</strong>, <strong>a*/b*</strong> is <strong>diverging</strong>, and the <strong>hue</strong> (azimuthal around a*=b*=0) is <strong>cyclic</strong>.  </p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="1018" height="513" src="/wp-content/uploads/2024/02/axes-1.png" alt="" class="wp-image-134" style="width:839px;height:auto" srcset="/wp-content/uploads/2024/02/axes-1.png 1018w, /wp-content/uploads/2024/02/axes-1-300x151.png 300w, /wp-content/uploads/2024/02/axes-1-768x387.png 768w, /wp-content/uploads/2024/02/axes-1-600x302.png 600w" sizes="(max-width: 1018px) 100vw, 1018px" /></figure>



<p>These most relevant categories, are shown as 2D planes in 3D <strong>L*a*b*</strong> space above.</p>



<p>Using the shapes above to slice the sRGB color gamut will produce 2D colormaps. </p>



<img decoding="async" src="https://trygvrad.github.io/plotly_2D_colormaps/cmaps.svg" style="width:820px;">
<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/3D_orangeBlue.html" height="200" width="185" frameborder="0" allow="fullscreen"></iframe>
<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/3D_cut.html" height="200" width="185" frameborder="0" allow="fullscreen"></iframe>
<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/3D_barrel.html" height="200" width="185" frameborder="0" allow="fullscreen"></iframe>
<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/3D_peak.html" height="200" width="185" frameborder="0" allow="fullscreen"></iframe>



<p>Note that defining colormaps in <strong>L*a*b*</strong>-space limits how much of the color gamut we can utilize. Colormaps defined in sRGB space provide a better utilization of the color gamut. Consider the following blue-orange bi-sequential colormaps:</p>



<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/orange_blue_Lab.html" height="300" width="285" frameborder="0" allow="fullscreen" scrolling="no" ></iframe>
<img decoding="async" src="https://trygvrad.github.io/plotly_2D_colormaps/defined_in_mod.svg" style="height:300px;">
<iframe loading="lazy" src="https://trygvrad.github.io/plotly_2D_colormaps/orange_blue_RGB.html" height="300" width="285" frameborder="0" allow="fullscreen" scrolling="no" ></iframe>



<p>The colormap defined in sRGB space has increased saturation because it is not limited by perceptual uniformity (i.e. the maximum orange has higher <strong>L*</strong> than the maximum blue). For many applications, the loss of perceptual uniformity is an acceptable trade for increased saturation (making the colormap &#8216;pop&#8217; more).</p>



<p>Additional colormaps can be generated using the same design principles:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="750" src="/wp-content/uploads/2024/02/cmaps2-1024x750.png" alt="" class="wp-image-152" srcset="/wp-content/uploads/2024/02/cmaps2-1024x750.png 1024w, /wp-content/uploads/2024/02/cmaps2-300x220.png 300w, /wp-content/uploads/2024/02/cmaps2-768x563.png 768w, /wp-content/uploads/2024/02/cmaps2-600x440.png 600w, /wp-content/uploads/2024/02/cmaps2.png 1250w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The circular colormaps are useful for applications where the data has rotational symmetry. </p>



<p class="has-large-font-size">3D colormaps &#8211; <strong>alpha</strong>, <strong>a*</strong> and <strong>b*</strong></p>



<p>One can design a 3D colormap based on the three axes <strong>L*</strong>, <strong>a*</strong> and <strong>b*</strong>. On a black background <strong>L*</strong> can be replaced by an <strong>alpha</strong> channel, if the colormap is constant in <strong>L*</strong>. The colormaps <em>&#8216;flat&#8217;</em> and <em>&#8216;disk&#8217;</em> (above) are designed to be used in this way.</p>



<p class="has-large-font-size">Adapting for colorblindness</p>



<p>It is not possible to fully accommodate the various kinds of colorblindness in multi-dimensional colormaps. However, it is worth noting all the common forms of color-blindness affect primarily the <strong>a</strong>* (red-green) axis in <strong>L*a*b*</strong> colorspace. Thus, when we design colormaps by slicing <strong>L*a*b*</strong> space, we should preferentially slice along the <strong>b</strong>* (blue-yellow) axis, as is done above for the colormaps &#8216;orangeBlue&#8217; and &#8216;cut&#8217;. However, when making use of a the <strong>hue</strong> in a cyclic colormap, this is not possible. </p>



<p>However, the hue can be cycled freely, and for the relevant colormaps the axes can be chosen such that the main features of the data are accessible also to those with common forms of colorblindness. The following figure shows the same data as seen by someone with normal color vision, and someone whom is red-green colorblind. For both cases, the data is shown with two different rotations of the colormap. With normal color vision, it is easily discernible that the two circled regions have different hue, but for someone who is red-green colorblind, this is only apparent if the colormap is rotated so that they are differentiated along the blue-yellow axis.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="442" src="/wp-content/uploads/2024/02/path166-1024x442.png" alt="" class="wp-image-136" srcset="/wp-content/uploads/2024/02/path166-1024x442.png 1024w, /wp-content/uploads/2024/02/path166-300x130.png 300w, /wp-content/uploads/2024/02/path166-768x332.png 768w, /wp-content/uploads/2024/02/path166-1536x663.png 1536w, /wp-content/uploads/2024/02/path166-1400x605.png 1400w, /wp-content/uploads/2024/02/path166-600x259.png 600w, /wp-content/uploads/2024/02/path166.png 1614w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The rotation of the colormap is therefore of critical importance for accommodating those with common forms of colorblindness. </p>



<p>For reference, the above colormaps are shown below as they appear for someone with deuteranopia (unable to perceive ‘green’ light – the most common form of color blindness).</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="750" src="/wp-content/uploads/2024/02/cmaps2CB-1024x750.png" alt="" class="wp-image-153" srcset="/wp-content/uploads/2024/02/cmaps2CB-1024x750.png 1024w, /wp-content/uploads/2024/02/cmaps2CB-300x220.png 300w, /wp-content/uploads/2024/02/cmaps2CB-768x563.png 768w, /wp-content/uploads/2024/02/cmaps2CB-600x440.png 600w, /wp-content/uploads/2024/02/cmaps2CB.png 1250w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="has-large-font-size">Tools used:</p>



<p><a href="https://colorspacious.readthedocs.io/en/latest/index.html">colorspacious</a> to convert between colorspaces and simulated colorblindness<br><a href="https://plotly.com/">plotly</a> to make 3d figures<br><a href="https://matplotlib.org/">matplotlib</a> to make 2d figures<br>The colormaps were originally made for <a href="https://colorstamps.readthedocs.io/en/latest/index.html">colorstamps</a></p>



<p class="has-large-font-size">Other resources:</p>



<p class="has-small-font-size">Bernard, Jürgen, et al. &#8220;A survey and task-based quality assessment of static 2D colormaps.&#8221; <em>Visualization and Data Analysis 2015</em>. Vol. 9397. SPIE, 2015.</p>



<p class="has-small-font-size">Strode, Georgianna, et al. &#8220;Operationalizing Trumbo’s principles of bivariate choropleth map design.&#8221; <em>Cartographic perspectives</em> 94 (2019): 5-24.</p>



<p class="has-small-font-size">Kruse, Andrew W., et al. &#8220;User study comparing linearity and orthogonalization for polarimetric visualizations.&#8221; <em>IEEE Access</em> 10 (2022): 28308-28321.<br></p>
]]></content:encoded>
					
					<wfw:commentRss>/designing-2d-colormaps/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Visualizing Dark-Field X-ray Microscopy</title>
		<link>/visualizing-dark-field-x-ray-microscopy/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Wed, 06 Dec 2023 11:39:32 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=93</guid>

					<description><![CDATA[A DF-XRM experiment deals with experimental geometry, sample geometry, real space and reciprocal space. To aid experimental planning I present my DF-XRM visualization app, with source code on github. There is an associated publication in the Journal of Open Source Software]]></description>
										<content:encoded><![CDATA[
<p>A DF-XRM experiment deals with experimental geometry, sample geometry, real space and reciprocal space. To aid experimental planning I present my DF-XRM visualization <a href="https://df-xrm.streamlit.app/">app</a>, with source code on <a href="https://github.com/trygvrad/DF-XRM_viz">github</a>.</p>



<p>There is an associated publication in the <a href="https://joss.theoj.org/papers/10.21105/joss.05177">Journal of Open Source Software</a></p>



<span id="more-93"></span>



<figure class="wp-block-image"><a href="https://doi.org/10.21105/joss.05177"><img decoding="async" src="https://joss.theoj.org/papers/10.21105/joss.05177/status.svg" alt="DOI"/></a></figure>



<figure class="wp-block-image"><img decoding="async" src="https://trygvrad.github.io/wp-content/uploads/2023/06/dfxrm_visualization_example-413x1024.png" alt="" class="wp-image-83"/></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Marie Skłodowska-Curie project in 1 minute</title>
		<link>/project-in-1-minute/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Tue, 13 Jun 2023 22:32:56 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=75</guid>

					<description><![CDATA[Here I describe my Marie Skłodowska–Curie project in 1 minute.Thank you to the Eurotech foundation for prodding me to make this video.]]></description>
										<content:encoded><![CDATA[
<p>Here I describe my Marie Skłodowska–Curie project in 1 minute.<br>Thank you to the Eurotech foundation for prodding me to make this video.<br></p>



<span id="more-75"></span>



<figure class="wp-block-video"><video controls src="http://localhost/wp-content/uploads/2023/06/TMR.mp4"></video></figure>
]]></content:encoded>
					
		
		<enclosure url="http://localhost/wp-content/uploads/2023/06/TMR.mp4" length="9698819" type="video/mp4" />

			</item>
		<item>
		<title>Colorstamps – 2d colormaps</title>
		<link>/colorstamps-2d-colormaps/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Sun, 25 Sep 2022 22:16:49 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=67</guid>

					<description><![CDATA[Colorstamps is a python package hosted on github and pypi for 2d colorstamps in defined as geometric shapes in the CAM02-LCD colorspace. Documentation at readthedocs. The package is inspired by the uniform colormaps in matplotlib, which has has greatly improved the visualization of scalar scientific image data. The project contains a number of 2d colormaps... <a class="more-link" href="/colorstamps-2d-colormaps/#more-67">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>Colorstamps is a python package hosted on <a href="https://github.com/trygvrad/colorstamps/">github</a> and <a href="https://pypi.org/project/colorstamps/">pypi</a> for 2d colorstamps in defined as geometric shapes in the CAM02-LCD <a href="https://github.com/njsmith/colorspacious">colorspace</a>. Documentation at <a href="https://colorstamps.readthedocs.io/en/latest/">readthedocs</a>. The package is inspired by the uniform colormaps in <a href="https://matplotlib.org/stable/tutorials/colors/colormaps.html">matplotlib</a>, which has has greatly improved the visualization of scalar scientific image data.</p>



<span id="more-67"></span>



<p>The project contains a number of 2d colormaps (stamps), suitable for different data types, it is useful to distinguish between data of like-type, and data of different type, and also unimodal (only positive) and bimodal (positive and negative) data.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="425" height="1024" src="http://localhost/wp-content/uploads/2023/06/colormaps-425x1024.png" alt="" class="wp-image-68" srcset="/wp-content/uploads/2023/06/colormaps-425x1024.png 425w, /wp-content/uploads/2023/06/colormaps-125x300.png 125w, /wp-content/uploads/2023/06/colormaps-600x1445.png 600w, /wp-content/uploads/2023/06/colormaps.png 615w" sizes="(max-width: 425px) 100vw, 425px" /></figure>



<p>• If both axes are bimodal, and of like type, the axis should have a round shape with an origin in the middle, and ‘disk’/’cone’/’funnel’ are suitable.<br>• With one unimodal and one bimodal axis (i.e. intensity + phase shift etc.) ‘cut’ is a suitable colormap, and this exists in mulitple variations. If one axis is circular, ‘barrel’ loops on the x-axis.<br>• With two unimodal axis, ‘orangeBlue’ is recommended.</p>



<p>Data outside the colormap must be projected into the valid space, and there are multiple options.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="455" height="208" src="http://localhost/wp-content/uploads/2023/06/point_outside_colormap.webp" alt="" class="wp-image-72" srcset="/wp-content/uploads/2023/06/point_outside_colormap.webp 455w, /wp-content/uploads/2023/06/point_outside_colormap-300x137.webp 300w" sizes="(max-width: 455px) 100vw, 455px" /></figure>



<p>Finally, the RGB colorspace on a computer can only represent a fraction of the CAM02-LCD colorspace, and colorstamps will limit the colors in the colormap to fit the RGB colorspace. A tool for evaluation of 2d colormaps are included.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="589" height="130" src="http://localhost/wp-content/uploads/2023/06/eval_peak.png" alt="" class="wp-image-71" srcset="/wp-content/uploads/2023/06/eval_peak.png 589w, /wp-content/uploads/2023/06/eval_peak-300x66.png 300w" sizes="(max-width: 589px) 100vw, 589px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="589" height="130" src="http://localhost/wp-content/uploads/2023/06/eval_hsv-1.png" alt="" class="wp-image-70" srcset="/wp-content/uploads/2023/06/eval_hsv-1.png 589w, /wp-content/uploads/2023/06/eval_hsv-1-300x66.png 300w" sizes="(max-width: 589px) 100vw, 589px" /></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Intedigitated electrodes</title>
		<link>/intedigitated-electrodes/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Fri, 23 Sep 2022 22:13:08 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=62</guid>

					<description><![CDATA[I built a model for the electric field in thin-films with interdigitated electrodes. An interactive demo is hosted at Streamlit, while the source is hosted on Github. The model is presented in A unified approach for the calculation of in-plane dielectric constant of films with interdigitated electrodes. This work builds upon a knowledge base of... <a class="more-link" href="/intedigitated-electrodes/#more-62">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<p>I built a model for the electric field in thin-films with interdigitated electrodes.</p>



<p>An interactive demo is hosted at <a href="https://interdigitated-electrodes.streamlit.app/">Streamlit</a>, while the source is hosted on <a href="https://github.com/trygvrad/Interdigitated-Electrodes">Github</a>.</p>



<p>The model is presented in <a href="https://iopscience.iop.org/article/10.1088/1361-665X/abb4b9">A unified approach for the calculation of in-plane dielectric constant of films with interdigitated electrodes</a>.</p>



<span id="more-62"></span>



<p>This work builds upon a knowledge base of a a series of works detailing how to accurately characterize in-plane ferroelectric properties of thin films, started in the group of Paul Muralt at EPFL, with works such as <a href="https://ieeexplore.ieee.org/document/6297833">1</a> <a href="https://aip.scitation.org/doi/full/10.1063/1.4983772">2</a> and <a href="https://iopscience.iop.org/article/10.1088/1361-6463/aab2a1/meta">3</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Mailsail – A Sailing Game</title>
		<link>/mailsail-a-sailing-game/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Wed, 21 Sep 2022 22:11:48 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=58</guid>

					<description><![CDATA[Mailsail is a sailing game made during a 2-week game jam in 2022.The game is fully playable in the browser at https://trygvrad.itch.io/mailsail]]></description>
										<content:encoded><![CDATA[
<p>Mailsail is a sailing game made during a 2-week game jam in 2022.<br>The game is fully playable in the browser at <a href="https://trygvrad.itch.io/mailsail">https://trygvrad.itch.io/mailsail</a></p>



<span id="more-58"></span>



<iframe allowfullscreen="true" scrolling="no" allow="autoplay; fullscreen *; geolocation; microphone; camera; midi; monetization; xr-spatial-tracking; gamepad; gyroscope; accelerometer; xr; cross-origin-isolated" id="game_drop" allowtransparency="true" webkitallowfullscreen="true" mozallowfullscreen="true" msallowfullscreen="true" src="https://v6p9d9t4.ssl.hwcdn.net/html/6467501/index.html" style="width: 604.583px; height: 780px;" frameborder="0">
</iframe>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Crystal structures as vector graphics</title>
		<link>/crystal-structures-as-vector-graphics/</link>
		
		<dc:creator><![CDATA[trygvrad]]></dc:creator>
		<pubDate>Mon, 19 Sep 2022 22:02:57 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://localhost/?p=53</guid>

					<description><![CDATA[Pychrysvis is a Streamlit app for making vector graphics from crystal structures (.cif files), with source on github. The you can then edit the vector graphics further in Inkscape, and make more advanced figures. like an explanation of dark-field X-ray microscopy: Diamond viewed along different axes: I prefer to work with vector graphics wherever possible,... <a class="more-link" href="/crystal-structures-as-vector-graphics/#more-53">Continue Reading &#8594;</a>]]></description>
										<content:encoded><![CDATA[
<span id="more-53"></span>



<p>Pychrysvis is a <a href="https://trygvrad-pychrysvis-pychrysvis-streamlit-wbewzd.streamlitapp.com/">Streamlit app</a> for making vector graphics from crystal structures (.cif files), with source on <a href="https://github.com/trygvrad/pychrysvis/">github</a>.</p>



<p>The you can then edit the vector graphics further in Inkscape, and make more advanced figures.</p>



<p>like an explanation of dark-field X-ray microscopy:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="642" src="http://localhost/wp-content/uploads/2023/06/DFXRM_modes-1024x642.png" alt="" class="wp-image-55" srcset="/wp-content/uploads/2023/06/DFXRM_modes-1024x642.png 1024w, /wp-content/uploads/2023/06/DFXRM_modes-300x188.png 300w, /wp-content/uploads/2023/06/DFXRM_modes-768x482.png 768w, /wp-content/uploads/2023/06/DFXRM_modes-600x376.png 600w, /wp-content/uploads/2023/06/DFXRM_modes-945x593.png 945w, /wp-content/uploads/2023/06/DFXRM_modes.png 1500w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Diamond viewed along different axes:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="219" src="http://localhost/wp-content/uploads/2023/06/diamond-1024x219.png" alt="" class="wp-image-56" srcset="/wp-content/uploads/2023/06/diamond-1024x219.png 1024w, /wp-content/uploads/2023/06/diamond-300x64.png 300w, /wp-content/uploads/2023/06/diamond-768x165.png 768w, /wp-content/uploads/2023/06/diamond-1536x329.png 1536w, /wp-content/uploads/2023/06/diamond-600x129.png 600w, /wp-content/uploads/2023/06/diamond-945x202.png 945w, /wp-content/uploads/2023/06/diamond.png 1867w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>I prefer to work with vector graphics wherever possible, and I was not able to find a suitable tool online, so I built this tool from leftover scraps of other projects.</p>



<p>There is minimal documentation, but perhaps someone else will find it useful nonetheless.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
