{"id":1311,"date":"2015-04-03T16:40:04","date_gmt":"2015-04-03T20:40:04","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=1311"},"modified":"2019-11-01T11:39:56","modified_gmt":"2019-11-01T15:39:56","slug":"displaying-a-color-gamut-surface","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2015\/04\/03\/displaying-a-color-gamut-surface\/","title":{"rendered":"Displaying a color gamut surface"},"content":{"rendered":"<div class=\"content\"><p>Today I'll show you one way to visualize the sRGB color gamut in L*a*b* space with assistance with a couple of new functions introduced last fall in the R2014b release. (I originally planned to post this a few months ago, but I got sidetracked writing about <a href=\"https:\/\/blogs.mathworks.com\/steve\/category\/colormap\/\">colormaps<\/a>.)<\/p><p>The first new function is called <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/boundary.html\"><tt>boundary<\/tt><\/a>, and it is in MATLAB. Given a set of 2-D or 3-D points, <tt>boundary<\/tt> computes, well, the boundary.<\/p><p>Here's an example to illustrate.<\/p><pre class=\"codeinput\">x = gallery(<span class=\"string\">'uniformdata'<\/span>,30,1,1);\r\ny = gallery(<span class=\"string\">'uniformdata'<\/span>,30,1,10);\r\nplot(x,y,<span class=\"string\">'.'<\/span>)\r\naxis([-0.2 1.2 -0.2 1.2])\r\naxis <span class=\"string\">equal<\/span>\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_01.png\" alt=\"\"> <p>Now compute and plot the boundary around the points.<\/p><pre class=\"codeinput\">k = boundary(x,y);\r\nhold <span class=\"string\">on<\/span>\r\nplot(x(k),y(k))\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_02.png\" alt=\"\"> <p>\"But, Steve,\" some of you are saying, \"that's not the only possible boundary around these points, right?\"<\/p><p>Right. The function <tt>boundary<\/tt> has an optional shrink factor that you can specify. A shrink factor of 0 corresponds to the convex hull. A shrink factor of 1 gives a compact boundary that envelops all the points.<\/p><pre class=\"codeinput\">k0 = boundary(x,y,0);\r\nk1 = boundary(x,y,1);\r\nhold <span class=\"string\">on<\/span>\r\nplot(x(k0),y(k0))\r\nplot(x(k1),y(k1))\r\nhold <span class=\"string\">off<\/span>\r\nlegend(<span class=\"string\">'Original points'<\/span>,<span class=\"string\">'Shrink factor: 0.5 (default)'<\/span>,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Shrink factor: 0'<\/span>,<span class=\"string\">'Shrink factor: 1'<\/span>)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_03.png\" alt=\"\"> <p>Here's a 3-D example using <tt>boundary<\/tt>. First, the points:<\/p><pre class=\"codeinput\">P = gallery(<span class=\"string\">'uniformdata'<\/span>,30,3,5);\r\nplot3(P(:,1),P(:,2),P(:,3),<span class=\"string\">'.'<\/span>,<span class=\"string\">'MarkerSize'<\/span>,10)\r\ngrid <span class=\"string\">on<\/span>\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_04.png\" alt=\"\"> <p>Now the boundary, plotted using <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/trisurf.html\"><tt>trisurf<\/tt><\/a>:<\/p><pre class=\"codeinput\">k = boundary(P);\r\nhold <span class=\"string\">on<\/span>\r\ntrisurf(k,P(:,1),P(:,2),P(:,3),<span class=\"string\">'Facecolor'<\/span>,<span class=\"string\">'red'<\/span>,<span class=\"string\">'FaceAlpha'<\/span>,0.1)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_05.png\" alt=\"\"> <p>The second new function I wanted to mention is <a href=\"https:\/\/www.mathworks.com\/help\/images\/ref\/rgb2lab.html\"><tt>rgb2lab<\/tt><\/a>. This function is in the Image Processing Toolbox. The toolbox could convert from sRGB to L*a*b* before, but this function makes it a bit easier. (And, if you're interested, it supports not only sRGB but also Adobe RGB 1998).<\/p><p>Just for grins, let's reverse the a* and b* color coordinates for an image.<\/p><pre class=\"codeinput\">rgb = imread(<span class=\"string\">'peppers.png'<\/span>);\r\nimshow(rgb)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_06.png\" alt=\"\"> <pre class=\"codeinput\">lab = rgb2lab(rgb);\r\nlabp = lab(:,:,[1 3 2]);\r\nrgbp = lab2rgb(labp);\r\nimshow(rgbp)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_07.png\" alt=\"\"> <p>Now let's get to work on visualizing the sRGB gamut surface. The basic strategy is to make a grid of points in RGB space, transform them to L*a*b* space, and find the boundary. (We'll use the default shrink factor.)<\/p><pre class=\"codeinput\">[r,g,b] = meshgrid(linspace(0,1,50));\r\nrgb = [r(:), g(:), b(:)];\r\nlab = rgb2lab(rgb);\r\na = lab(:,2);\r\nb = lab(:,3);\r\nL = lab(:,1);\r\nk = boundary(a,b,L);\r\ntrisurf(k,a,b,L,<span class=\"string\">'FaceColor'<\/span>,<span class=\"string\">'interp'<\/span>,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'FaceVertexCData'<\/span>,rgb,<span class=\"string\">'EdgeColor'<\/span>,<span class=\"string\">'none'<\/span>)\r\nxlabel(<span class=\"string\">'a*'<\/span>)\r\nylabel(<span class=\"string\">'b*'<\/span>)\r\nzlabel(<span class=\"string\">'L*'<\/span>)\r\naxis([-110 110 -110 110 0 100])\r\nview(-10,35)\r\naxis <span class=\"string\">equal<\/span>\r\ntitle(<span class=\"string\">'sRGB gamut surface in L*a*b* space'<\/span>)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_08.png\" alt=\"\"> <p>Here are another couple of view angles.<\/p><pre class=\"codeinput\">view(75,20)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_09.png\" alt=\"\"> <pre class=\"codeinput\">view(185,15)\r\n<\/pre><img decoding=\"async\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2015\/srgb_gamut_surface_10.png\" alt=\"\"> <p>That's it for this week. Have fun with color-space surfaces!<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_572c2a833e74401ea84693131262123a() {\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='572c2a833e74401ea84693131262123a ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 572c2a833e74401ea84693131262123a';\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 2015 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_572c2a833e74401ea84693131262123a()\"><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; R2015a<br><\/p><\/div><!--\r\n572c2a833e74401ea84693131262123a ##### SOURCE BEGIN #####\r\n%%\r\n% Today I'll show you one way to visualize the sRGB color gamut in L*a*b* space\r\n% with assistance with a couple of new functions introduced last fall in the\r\n% R2014b release. (I originally planned to post this a few months ago, but I got\r\n% sidetracked writing about <https:\/\/blogs.mathworks.com\/steve\/category\/colormap\/\r\n% colormaps>.)\r\n%\r\n% The first new function is called\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/boundary.html |boundary|>, and it is\r\n% in MATLAB. Given a set of 2-D or 3-D points, |boundary| computes, well, the\r\n% boundary.\r\n%\r\n% Here's an example to illustrate.\r\n\r\nx = gallery('uniformdata',30,1,1);\r\ny = gallery('uniformdata',30,1,10);\r\nplot(x,y,'.')\r\naxis([-0.2 1.2 -0.2 1.2])\r\naxis equal\r\n\r\n%%\r\n% Now compute and plot the boundary around the points.\r\n\r\nk = boundary(x,y);\r\nhold on\r\nplot(x(k),y(k))\r\nhold off\r\n\r\n%%\r\n% \"But, Steve,\" some of you are saying, \"that's not the only possible boundary\r\n% around these points, right?\"\r\n%\r\n% Right. The function |boundary| has an optional shrink factor that you can\r\n% specify. A shrink factor of 0 corresponds to the convex hull. A shrink factor\r\n% of 1 is the smallest polygonal region containing all the points.\r\n\r\nk0 = boundary(x,y,0);\r\nk1 = boundary(x,y,1);\r\nhold on\r\nplot(x(k0),y(k0))\r\nplot(x(k1),y(k1))\r\nhold off\r\nlegend('Original points','Shrink factor: 0.5 (default)',...\r\n    'Shrink factor: 0','Shrink factor: 1')\r\n\r\n%%\r\n% Here's a 3-D example using |boundary|. First, the points:\r\n\r\nP = gallery('uniformdata',30,3,5);\r\nplot3(P(:,1),P(:,2),P(:,3),'.','MarkerSize',10)\r\ngrid on\r\n\r\n%%\r\n% Now the boundary, plotted using <https:\/\/www.mathworks.com\/help\/matlab\/ref\/trisurf.html \r\n% |trisurf|>:\r\n\r\nk = boundary(P);\r\nhold on\r\ntrisurf(k,P(:,1),P(:,2),P(:,3),'Facecolor','red','FaceAlpha',0.1)\r\nhold off\r\n\r\n%%\r\n% The second new function I wanted to mention is <https:\/\/www.mathworks.com\/help\/images\/ref\/rgb2lab.html \r\n% |rgb2lab|>. This function is in the Image Processing Toolbox. The toolbox\r\n% could convert from sRGB to L*a*b* before, but this function makes it a bit\r\n% easier. (And, if you're interested, it supports not only sRGB but also Adobe\r\n% RGB 1998).\r\n%\r\n% Just for grins, let's reverse the a* and b* color coordinates for an image.\r\n\r\nrgb = imread('peppers.png');\r\nimshow(rgb)\r\n\r\n%%\r\n\r\nlab = rgb2lab(rgb);\r\nlabp = lab(:,:,[1 3 2]);\r\nrgbp = lab2rgb(labp);\r\nimshow(rgbp)\r\n\r\n%%\r\n% Now let's get to work on visualizing the sRGB gamut surface. The basic\r\n% strategy is to make a grid of points in RGB space, transform them to L*a*b*\r\n% space, and find the boundary. (We'll use the default shrink factor.)\r\n\r\n\r\n[r,g,b] = meshgrid(linspace(0,1,50));\r\nrgb = [r(:), g(:), b(:)];\r\nlab = rgb2lab(rgb);\r\na = lab(:,2);\r\nb = lab(:,3);\r\nL = lab(:,1);\r\nk = boundary(a,b,L);\r\ntrisurf(k,a,b,L,'FaceColor','interp',...\r\n    'FaceVertexCData',rgb,'EdgeColor','none')\r\nxlabel('a*')\r\nylabel('b*')\r\nzlabel('L*')\r\naxis([-110 110 -110 110 0 100])\r\nview(-10,35)\r\naxis equal\r\ntitle('sRGB gamut surface in L*a*b* space')\r\n\r\n%%\r\n% Here are another couple of view angles.\r\n\r\nview(75,20)\r\n\r\n%%\r\n\r\nview(185,15)\r\n\r\n%%\r\n% That's it for this week. Have fun with color-space surfaces!\r\n\r\n##### SOURCE END ##### 572c2a833e74401ea84693131262123a\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/srgb_gamut_surface_08.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Today I'll show you one way to visualize the sRGB color gamut in L*a*b* space with assistance with a couple of new functions introduced last fall in the R2014b release. (I originally planned to post... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2015\/04\/03\/displaying-a-color-gamut-surface\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":1313,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[50,1109,1009,70,90,76,36,1115,92,32,30,68,1113,1111,216,94,96,1117],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1311"}],"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=1311"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1311\/revisions"}],"predecessor-version":[{"id":2979,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/1311\/revisions\/2979"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/1313"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=1311"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=1311"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=1311"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}