{"id":2660,"date":"2017-07-24T15:57:47","date_gmt":"2017-07-24T19:57:47","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=2660"},"modified":"2022-08-24T17:01:29","modified_gmt":"2022-08-24T21:01:29","slug":"colormap-test-image","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2017\/07\/24\/colormap-test-image\/","title":{"rendered":"Colormap Test Image"},"content":{"rendered":"<div class=\"content\"><p>Today I want to tell you how and why I made these images:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap-test-image-screenshot.png\" alt=\"\"> <\/p><p>After the MATLAB R2014b release, I wrote several blog posts (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/10\/13\/a-new-colormap-for-matlab-part-1-introduction\/\">Part 1<\/a>, <a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/10\/20\/a-new-colormap-for-matlab-part-2-troubles-with-rainbows\/\">Part 2<\/a>, <a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/11\/12\/a-new-colormap-for-matlab-part-3-some-reactions\/\">Part 3<\/a>, and <a href=\"https:\/\/blogs.mathworks.com\/steve\/2014\/12\/10\/a-new-colormap-for-matlab-part-4-the-name\/\">Part 4<\/a>) about the new default colormap, <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/parula.html\">parula<\/a>, that was introduced in that release.<\/p><p>Sometime later, I came across some material by Peter Kovesi about <a href=\"http:\/\/peterkovesi.com\/projects\/colourmaps\/\">designing perceptually uniform colormaps<\/a> (or colourmaps, as Peter writes it).<\/p><p>I was particularly intrigued by a test image that Peter designed for the purpose of visually evaluating the perceptual characteristics of a colormap.<\/p><p>The image is constructed by superimposing a sinusoid on a linear ramp, with the amplitude of the sinusoid getting smaller as you move away from the top row. Here are three cross-sections of the image: row 1, row 64, and row 128.<\/p><pre class=\"codeinput\">url = <span class=\"string\">'http:\/\/peterkovesi.com\/projects\/colourmaps\/cm_MATLAB_gray256.png'<\/span>;\r\nI = im2double(imread(url));\r\n\r\nsubplot(3,1,1)\r\nplot(I(1,:))\r\naxis([1 512 0 1])\r\ntitle(<span class=\"string\">'Row 1'<\/span>)\r\n\r\nsubplot(3,1,2)\r\nplot(I(64,:))\r\naxis([1 512 0 1])\r\ntitle(<span class=\"string\">'Row 64'<\/span>)\r\n\r\nsubplot(3,1,3)\r\nplot(I(128,:))\r\naxis([1 512 0 1])\r\ntitle(<span class=\"string\">'Row 128'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap_test_image_01.png\" alt=\"\"> <p>Here is the basic code for making this test image. I'm going to vary Kovesi's image slightly. I'll add an extra half-cycle of the sinusoid so that it reaches a peak at the right side of the image, and I'll add a full-range linear ramp section at the bottom. (If you look closely at the cross-section curves above, you'll see that the linear ramp goes from 5% to 95% of the range.)<\/p><p>First, compute the ramp.<\/p><pre class=\"codeinput\">num_cycles = 64.5;\r\npixels_per_cycle = 8;\r\nA = 0.05;\r\nwidth = pixels_per_cycle * num_cycles + 1;\r\nheight = round((width - 1) \/ 4);\r\nramp = linspace(A, 1-A, width);\r\n<\/pre><p>Next, compute the sinusoid.<\/p><pre class=\"codeinput\">k = 0:(width-1);\r\nx = -A*cos((2*pi\/pixels_per_cycle) * k);\r\n<\/pre><p>Now, vary the amplitude of the sinusoid with the square of the distance from the bottom of the image.<\/p><pre class=\"codeinput\">q = 0:(height-1);\r\ny = ((height - q) \/ (height - 1)).^2;\r\nI1 = (y') .* x;\r\n<\/pre><p>Superimpose the sinusoid on the ramp.<\/p><pre class=\"codeinput\">I = I1 + ramp;\r\n<\/pre><p>Finally, add a full-range linear ramp section to the bottom of the image.<\/p><pre class=\"codeinput\">I = [I ; repmat(linspace(0,1,width), round(height\/4), 1)];\r\n\r\nclf\r\nimshow(I)\r\ntitle(<span class=\"string\">'Colormap test image'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap_test_image_02.png\" alt=\"\"> <p>Last week, I posted <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/63726-colormap-test-image\">Colormap Test Image<\/a> to the File Exchange. It contains the function <tt>colormapTestImage<\/tt>, which does all this for you.<\/p><pre class=\"codeinput\">I = colormapTestImage;\r\n<\/pre><p>The function has another syntax, too. If you pass it the name of a colormap, it will display the test image using that colormap. For example, here is the test image with the old MATLAB default colormap, <tt>jet<\/tt>.<\/p><pre class=\"codeinput\">colormapTestImage(<span class=\"string\">'jet'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap_test_image_03.png\" alt=\"\"> <p>This test image illustrates why we replaced <tt>jet<\/tt> as the default MATLAB colormap. I have annotated the image below to show some of the issues.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/jet-test-image-annotated.png\" alt=\"\"> <\/p><p>Now compare with the new default colormap, <tt>parula<\/tt>.<\/p><pre class=\"codeinput\">colormapTestImage(<span class=\"string\">'parula'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap_test_image_04.png\" alt=\"\"> <p>I think that illustrates what we were trying to achieve with <tt>parula<\/tt>: perceptual fidelity to the data.<\/p><p>Since I'm talking about <tt>parula<\/tt>, I'll finish by mentioning that we need some very subtle tweaks to <tt>parula<\/tt> in the R2017b release. So you can compare, I'll show you the original version that shipped with R2014b.<\/p><pre class=\"codeinput\">colormapTestImage(<span class=\"string\">'parula_original'<\/span>)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap_test_image_05.png\" alt=\"\"> <p>Readers, can you tell what is different? Let us know in the comments.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_17a638dae0bb42cdae93c6e4992f2d18() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='17a638dae0bb42cdae93c6e4992f2d18 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 17a638dae0bb42cdae93c6e4992f2d18';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2017 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_17a638dae0bb42cdae93c6e4992f2d18()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2017a<br><\/p><\/div><!--\r\n17a638dae0bb42cdae93c6e4992f2d18 ##### SOURCE BEGIN #####\r\n%%\r\n% Today I want to tell you how and why I made these images:\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/steve\/files\/colormap-test-image-screenshot.png>>\r\n%\r\n% After the MATLAB R2014b release, I wrote several blog posts \r\n% (<https:\/\/blogs.mathworks.com\/steve\/2014\/10\/13\/a-new-colormap-for-matlab-part-1-introduction\/ Part 1>, \r\n% <https:\/\/blogs.mathworks.com\/steve\/2014\/10\/20\/a-new-colormap-for-matlab-part-2-troubles-with-rainbows\/\r\n% Part 2>,\r\n% <https:\/\/blogs.mathworks.com\/steve\/2014\/11\/12\/a-new-colormap-for-matlab-part-3-some-reactions\/\r\n% Part 3>, and\r\n% <https:\/\/blogs.mathworks.com\/steve\/2014\/12\/10\/a-new-colormap-for-matlab-part-4-the-name\/\r\n% Part 4>)\r\n% about the new\r\n% default colormap, \r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/parula.html \r\n% parula>, that was introduced in that release.\r\n%\r\n% Sometime later, I came across some material by Peter Kovesi about\r\n% <http:\/\/peterkovesi.com\/projects\/colourmaps\/ \r\n% designing perceptually uniform colormaps> (or colourmaps, as Peter writes\r\n% it). \r\n%\r\n% I was particularly intrigued by a test image that Peter designed for the\r\n% purpose of visually evaluating the perceptual characteristics of a\r\n% colormap. Here is the test image:\r\n%\r\n% <<http:\/\/peterkovesi.com\/projects\/colourmaps\/cm_MATLAB_gray256.png>>\r\n%\r\n% The image is constructed by superimposing a sinusoid on a linear ramp,\r\n% with the amplitude of the sinusoid getting smaller as you move away from\r\n% the top row. Here are three cross-sections of the image: row 1, row 64,\r\n% and row 128.\r\n\r\nurl = 'http:\/\/peterkovesi.com\/projects\/colourmaps\/cm_MATLAB_gray256.png';\r\nI = im2double(imread(url));\r\n\r\nsubplot(3,1,1)\r\nplot(I(1,:))\r\naxis([1 512 0 1])\r\ntitle('Row 1')\r\n\r\nsubplot(3,1,2)\r\nplot(I(64,:))\r\naxis([1 512 0 1])\r\ntitle('Row 64')\r\n\r\nsubplot(3,1,3)\r\nplot(I(128,:))\r\naxis([1 512 0 1])\r\ntitle('Row 128')\r\n\r\n%%\r\n% Here is the basic code for making this test image. I'm going to vary\r\n% Kovesi's image slightly. I'll add an extra half-cycle of the\r\n% sinusoid so that it reaches a peak at the right side of the image, and\r\n% I'll add a full-range linear ramp section at the bottom. (If you\r\n% look closely at the cross-section curves above, you'll see that the\r\n% linear ramp goes from 5% to 95% of the range.)\r\n%\r\n% First, compute the ramp.\r\n\r\nnum_cycles = 64.5;\r\npixels_per_cycle = 8;\r\nA = 0.05;\r\nwidth = pixels_per_cycle * num_cycles + 1;\r\nheight = round((width - 1) \/ 4);\r\nramp = linspace(A, 1-A, width);\r\n\r\n%%\r\n% Next, compute the sinusoid.\r\n\r\nk = 0:(width-1);\r\nx = -A*cos((2*pi\/pixels_per_cycle) * k);\r\n\r\n%%\r\n% Now, vary the amplitude of the sinusoid with the square of the distance\r\n% from the bottom of the image.\r\n\r\nq = 0:(height-1);\r\ny = ((height - q) \/ (height - 1)).^2;\r\nI1 = (y') .* x;\r\n\r\n%%\r\n% Superimpose the sinusoid on the ramp.\r\n\r\nI = I1 + ramp;\r\n\r\n%%\r\n% Finally, add a full-range linear ramp section to the bottom of the image.\r\n\r\nI = [I ; repmat(linspace(0,1,width), round(height\/4), 1)];\r\n\r\nclf\r\nimshow(I)\r\ntitle('Colormap test image')\r\n\r\n%%\r\n% Last week, I posted <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/63726-colormap-test-image \r\n% Colormap Test Image> to the File Exchange. It contains\r\n% the function |colormapTestImage|, which does all this for you.\r\n\r\nI = colormapTestImage;\r\n\r\n%%\r\n% The function has another syntax, too. If you pass it the name of a\r\n% colormap, it will display the test image using that colormap. For\r\n% example, here is the test image with the old MATLAB default colormap,\r\n% |jet|.\r\n\r\ncolormapTestImage('jet')\r\n\r\n%%\r\n% This test image illustrates why we replaced |jet| as the default MATLAB\r\n% colormap. I have annotated the image below to show some of the issues.\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/steve\/files\/jet-test-image-annotated.png>>\r\n%\r\n% Now compare with the new default colormap, |parula|.\r\n\r\ncolormapTestImage('parula')\r\n\r\n%%\r\n% I think that illustrates what we were trying to achieve with |parula|:\r\n% perceptual fidelity to the data.\r\n%\r\n% Since I'm talking about |parula|, I'll finish by mentioning that we need\r\n% some very subtle tweaks to |parula| in the R2017b release. So you can compare,\r\n% I'll show you the original version that shipped with R2014b.\r\n\r\ncolormapTestImage('parula_original')\r\n\r\n%%\r\n% Readers, can you tell what is different? Let us know in the comments.\r\n\r\n\r\n##### SOURCE END ##### 17a638dae0bb42cdae93c6e4992f2d18\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/colormap-test-image-screenshot.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Today I want to tell you how and why I made these images: After the MATLAB R2014b release, I wrote several blog posts (Part 1, Part 2, Part 3, and Part 4) about the new default colormap, parula, that... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2017\/07\/24\/colormap-test-image\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":2658,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[24],"tags":[50,178,74,390,76,32,532,68,116,188,72,52],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2660"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=2660"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2660\/revisions"}],"predecessor-version":[{"id":5666,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2660\/revisions\/5666"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/2658"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=2660"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=2660"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=2660"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}