{"id":96,"date":"2006-11-24T07:00:46","date_gmt":"2006-11-24T12:00:46","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=96"},"modified":"2019-10-22T16:34:55","modified_gmt":"2019-10-22T20:34:55","slug":"tetrahedral-interpolation-for-colorspace-conversion","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2006\/11\/24\/tetrahedral-interpolation-for-colorspace-conversion\/","title":{"rendered":"Tetrahedral interpolation for colorspace conversion"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p>The Image Processing Toolbox function <tt>applycform<\/tt> can convert colors based on ICC profiles.  ICC standards for the <a href=\"http:\/\/www.color.org\">International Color Consortium<\/a>, and an ICC profile typically contains information about the color characteristics of a particular device.  Specifically,\r\n      the profile contains data used to transform colors between the device-specific colorspace and the device-independent colorspaces\r\n      L*a*b* or XYZ.  These colorspace transformations can be quite complicated, and a profile may contain parametric curves, matrix\r\n      multiplications, one-dimensional lookup tables, and multidimensional lookup tables.  Many contain all of the above.\r\n   <\/p>\r\n   <p><tt>applycform<\/tt> today uses trilinear interpolation for profiles containing three-dimensional lookup tables (or quadrilinear for four-dimensional\r\n      tables).  Some of our color science customers have been recommending that we switch to something called <i>tetrahedral interpolation<\/i> instead.\r\n   <\/p>\r\n   <p>In both tetrahedral and trilinear interpolation the grid points along each of the three axes serve to divide the volume into\r\n      a set of rectangular hexahedra.  To interpolate at a particular point, the first step is determine which hexahedron the point\r\n      is in.  In tetrahedral interpolation, the hexahedron is further subdivided into six tetrahedra.  There is more than one possible\r\n      subdivision.  Here's the subdivision typically used for colorspace conversion applications.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% Form the eight vertices of a cube.<\/span>\r\nvertices = [0 0 0\r\n    1 0 0\r\n    0 0 1\r\n    1 0 1\r\n    0 1 0\r\n    1 1 0\r\n    0 1 1\r\n    1 1 1];<\/pre><p>One of the tetrahedra contains 1st, 2nd, 6th, and 8th vertices.  The function <tt>nchoosek<\/tt> gives us a convenient way to compute the corresponding faces.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">f = nchoosek([1 2 6 8], 3)<\/pre><pre style=\"font-style:oblique\">\r\nf =\r\n\r\n     1     2     6\r\n     1     2     8\r\n     1     6     8\r\n     2     6     8\r\n\r\n<\/pre><p>Here are the faces for all six of the tetrahedra.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">faces{1} = nchoosek([1 2 6 8], 3);\r\nfaces{2} = nchoosek([1 5 6 8], 3);\r\nfaces{3} = nchoosek([1 3 7 8], 3);\r\nfaces{4} = nchoosek([1 5 7 8], 3);\r\nfaces{5} = nchoosek([1 3 4 8], 3);\r\nfaces{6} = nchoosek([1 2 4 8], 3);\r\n\r\ncolors = jet(numel(faces));\r\n\r\nec = <span style=\"color: #A020F0\">'black'<\/span>;\r\nfa = 0.2;\r\n\r\nclose <span style=\"color: #A020F0\">all<\/span>\r\n<span style=\"color: #0000FF\">for<\/span> k = 1:numel(faces)\r\n    tetra{k} = patch(<span style=\"color: #A020F0\">'Vertices'<\/span>, vertices, <span style=\"color: #A020F0\">'Faces'<\/span>, faces{k}, <span style=\"color: #0000FF\">...<\/span>\r\n    <span style=\"color: #A020F0\">'FaceColor'<\/span>, colors(k,:), <span style=\"color: #A020F0\">'EdgeColor'<\/span>, ec, <span style=\"color: #A020F0\">'FaceAlpha'<\/span>, fa);\r\n<span style=\"color: #0000FF\">end<\/span>\r\n\r\nview(3)\r\naxis <span style=\"color: #A020F0\">equal<\/span><\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/96\/tetrahedral_interpolation_01.png\"> <p>That's a little hard to see, so let's show the individual tetrahedra in separate plots.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">fa = 0.4;\r\nea = 0.1;\r\n<span style=\"color: #0000FF\">for<\/span> k = 1:6\r\n    subplot(2,3,k)\r\n    <span style=\"color: #0000FF\">for<\/span> p = 1:6\r\n            tetra{p} = patch(<span style=\"color: #A020F0\">'Vertices'<\/span>, vertices, <span style=\"color: #A020F0\">'Faces'<\/span>, faces{p}, <span style=\"color: #0000FF\">...<\/span>\r\n              <span style=\"color: #A020F0\">'FaceColor'<\/span>, <span style=\"color: #A020F0\">'none'<\/span>, <span style=\"color: #A020F0\">'EdgeColor'<\/span>, ec, <span style=\"color: #A020F0\">'EdgeAlpha'<\/span>, ea);\r\n    <span style=\"color: #0000FF\">end<\/span>\r\n    set(tetra{k}, <span style=\"color: #A020F0\">'FaceColor'<\/span>, colors(k,:), <span style=\"color: #A020F0\">'FaceAlpha'<\/span>, fa);\r\n    axis <span style=\"color: #A020F0\">equal<\/span>\r\n    axis([0 1 0 1 0 1])\r\n    view(3)\r\n    grid <span style=\"color: #A020F0\">on<\/span>\r\n<span style=\"color: #0000FF\">end<\/span><\/pre>\r\n<img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/96\/tetrahedral_interpolation_02.png\"> <p>Once the tetrahedron containing the interpolation point is identified, the interpolation is computed as a weighted sum of\r\n      the grid values at the vertices of that tetrahedron.\r\n   <\/p>\r\n   <p>If you look carefully at each of the tetrahedra, you can see that each one shares a common edge that is the diagonal from\r\n      (0,0,0) to (1,1,1). That's why this particular tetrahedral subdivision is typically used for colorspace conversions.  In an\r\n      RGB space, this diagonal contains the neutral (or gray) colors, which are particularly important. Tetrahedral interpolation\r\n      can be used to convert colors more accurately and with lower computational cost.\r\n   <\/p>\r\n   <p>We now have a <a href=\"https:\/\/www.mathworks.com\/support\/bugreports\/details.html?rp=341909\">patch available online<\/a> that updates <tt>applycform<\/tt> to use tetrahedral interpolation.  If color science is your game and you use the ICC-related functions in the toolbox, then\r\n      I invite you to download and try the patch.\r\n   <\/p>\r\n   <p>If you want to know more about interpolation methods used for colorspace conversions, you might find this paper to be useful:<\/p>\r\n   <p>Kasson, Nin, Plouffe, and Hafner, \"Performing color space conversions with three-dimensional linear interpolation,\" <i>Journal of Electronic Imaging<\/i>, July 1995, Vol. 4(3), pp. 226-250.\r\n   <\/p><script language=\"JavaScript\"> \r\n<!--\r\n    function grabCode_b05d6498223f484a80cd79e752ad3718() {\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='b05d6498223f484a80cd79e752ad3718 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' b05d6498223f484a80cd79e752ad3718';\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        author = 'Steve Eddins';\r\n        copyright = 'Copyright 2006 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 author and copyright lines at the bottom if specified.\r\n        if ((author.length > 0) || (copyright.length > 0)) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (author.length > 0) {\r\n                d.writeln('% _' + author + '_');\r\n            }\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-->\r\n      <\/script>\r\n<noscript>\r\n<em>A JavaScript-enabled browser is required to use the \"Get the MATLAB code\" link.<\/em>\r\n<\/noscript>\r\n<p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_b05d6498223f484a80cd79e752ad3718()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code<\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.3<br><\/p>\r\n<\/div>\r\n<!--\r\nb05d6498223f484a80cd79e752ad3718 ##### SOURCE BEGIN #####\r\n%% Tetrahedral interpolation for colorspace conversion\r\n% The Image Processing Toolbox function <https:\/\/www.mathworks.com\/help\/images\/index.htmlapplycform.html\r\n% |applycform|> can convert colors\r\n% based on ICC profiles.  ICC standards for the <http:\/\/www.color.org \r\n% International Color\r\n% Consortium>, and an ICC profile typically contains information about the \r\n% color characteristics of a particular device.  Specifically, the profile\r\n% contains data used to transform colors between the device-specific\r\n% colorspace and the device-independent colorspaces L*a*b* or XYZ.  These\r\n% colorspace transformations can be quite complicated, and a profile may\r\n% contain parametric curves, matrix multiplications, one-dimensional lookup\r\n% tables, and multidimensional lookup tables.  Many contain all of the\r\n% above.\r\n%\r\n% |applycform| today uses trilinear interpolation for profiles containing\r\n% three-dimensional lookup tables (or quadrilinear for four-dimensional\r\n% tables).  Some of our color science customers have been recommending that\r\n% we switch to something called _tetrahedral interpolation_ instead.\r\n%\r\n% In both tetrahedral and trilinear interpolation the\r\n% grid points along each of the three axes serve to divide the volume into\r\n% a set of rectangular hexahedra.  To interpolate at a particular point,\r\n% the first step is determine which hexahedron the point is in.  In\r\n% tetrahedral interpolation, the hexahedron is further subdivided into six\r\n% tetrahedra.  There is more than one possible subdivision.  Here's the\r\n% subdivision typically used for colorspace conversion applications.\r\n\r\n% Form the eight vertices of a cube.\r\nvertices = [0 0 0\r\n    1 0 0\r\n    0 0 1\r\n    1 0 1\r\n    0 1 0\r\n    1 1 0\r\n    0 1 1\r\n    1 1 1];\r\n\r\n%%\r\n% One of the tetrahedra contains 1st, 2nd, 6th, and 8th vertices.  The \r\n% function |nchoosek| gives us a convenient way to compute the corresponding\r\n% faces.\r\n\r\nf = nchoosek([1 2 6 8], 3)\r\n\r\n%%\r\n% Here are the faces for all six of the tetrahedra.\r\nfaces{1} = nchoosek([1 2 6 8], 3);\r\nfaces{2} = nchoosek([1 5 6 8], 3);\r\nfaces{3} = nchoosek([1 3 7 8], 3);\r\nfaces{4} = nchoosek([1 5 7 8], 3);\r\nfaces{5} = nchoosek([1 3 4 8], 3);\r\nfaces{6} = nchoosek([1 2 4 8], 3);\r\n\r\ncolors = jet(numel(faces));\r\n\r\nfc = 'red';\r\nec = 'black';\r\nfa = 0.2;\r\n\r\nclose all\r\nfor k = 1:numel(faces)\r\n    tetra{k} = patch('Vertices', vertices, 'Faces', faces{k}, ...\r\n    'FaceColor', colors(k,:), 'EdgeColor', ec, 'FaceAlpha', fa);\r\nend\r\n\r\nview(3)\r\naxis equal\r\n\r\n%%\r\n% That's a little hard to see, so let's show the individual tetrahedra in\r\n% separate plots.\r\n\r\nfa = 0.4;\r\nea = 0.1\r\nfor k = 1:6\r\n    subplot(2,3,k)\r\n    for p = 1:6\r\n            tetra{p} = patch('Vertices', vertices, 'Faces', faces{p}, ...\r\n              'FaceColor', 'none', 'EdgeColor', ec, 'EdgeAlpha', ea);\r\n    end\r\n    set(tetra{k}, 'FaceColor', colors(k,:), 'FaceAlpha', fa);\r\n    axis equal\r\n    axis([0 1 0 1 0 1])\r\n    view(3)\r\n    grid on\r\nend\r\n\r\n%%\r\n% Once the tetrahedron containing the interpolation point is identified,\r\n% the interpolation is computed as a weighted sum of the grid values at the\r\n% vertices of that tetrahedron.\r\n%\r\n% If you look carefully at each of the tetrahedra, you can see that each\r\n% one shares a common edge that is the diagonal from (0,0,0) to (1,1,1).\r\n% That's why this particular tetrahedral subdivision is\r\n% typically used for colorspace conversions.  In an RGB space, this\r\n% diagonal contains the neutral (or gray) colors, which are particularly\r\n% important. Tetrahedral interpolation can be used to convert colors more\r\n% accurately and with lower computational cost.\r\n%\r\n% We now have a <https:\/\/www.mathworks.com\/support\/bugreports\/details.html?rp=341909 \r\n% patch available online> that updates |applycform| to use\r\n% tetrahedral interpolation.  If color science is your game and you use the\r\n% ICC-related functions in the toolbox, then I invite you to download and\r\n% try the patch.\r\n%\r\n% If you want to know more about interpolation methods used for colorspace\r\n% conversions, you might find this paper to be useful:\r\n%\r\n% Kasson, Nin, Plouffe, and Hafner, \"Performing color space conversions\r\n% with three-dimensional linear interpolation,\" _Journal of Electronic\r\n% Imaging_, July 1995, Vol. 4(3), pp. 226-250.\r\n\r\n\r\n##### SOURCE END ##### b05d6498223f484a80cd79e752ad3718\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   The Image Processing Toolbox function applycform can convert colors based on ICC profiles.  ICC standards for the International Color Consortium, and an ICC profile typically contains... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/11\/24\/tetrahedral-interpolation-for-colorspace-conversion\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[50,278,70,276,274,162,210,72,216],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/96"}],"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=96"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/96\/revisions"}],"predecessor-version":[{"id":2241,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/96\/revisions\/2241"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=96"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=96"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=96"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}