{"id":356,"date":"2010-12-23T16:06:36","date_gmt":"2010-12-23T21:06:36","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/2010\/12\/23\/two-dimensional-histograms\/"},"modified":"2019-10-29T13:44:52","modified_gmt":"2019-10-29T17:44:52","slug":"two-dimensional-histograms","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2010\/12\/23\/two-dimensional-histograms\/","title":{"rendered":"Two-Dimensional Histograms"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <p><a href=\"https:\/\/blogs.mathworks.com\/steve\/2010\/12\/17\/what-color-is-green\/\">Last week<\/a> I showed this picture of M&amp;Ms on my desk, and I used it to raise some vague questions about the definition of the color green.\r\n      Today I'll use this picture as an opportunity to demonstrate how to construct and visualize a two-dimensional histogram.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">url = <span style=\"color: #A020F0\">'https:\/\/blogs.mathworks.com\/images\/steve\/2010\/mms.jpg'<\/span>;\r\nrgb = imread(url);\r\nimshow(rgb)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2010\/two_dim_histogram_01.jpg\"> <p>As a preparatory step, I'll first convert the pixels from the sRGB color space to the L*a*b* color space. I've written about\r\n      the L*a*b* color before (<a href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/05\/09\/a-lab-based-uniform-color-scale\/\">09-May-2006<\/a>, <a href=\"https:\/\/blogs.mathworks.com\/steve\/2006\/06\/19\/hue-shifts-near-the-l0-axis\/\">19-Jun-2006<\/a>). It does a couple of nice things. First, it separates perceived brightness (L*) from color (a* and b*). Second, as a rough\r\n      approximation it is perceptually uniform. That is, Euclidean distances in the L*a*b* space correspond roughly uniformly to\r\n      perceived color differences.\r\n   <\/p>\r\n   <p>Here's how to do the conversion:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">lab = applycform(rgb, makecform(<span style=\"color: #A020F0\">'srgb2lab'<\/span>));<\/pre><p>But I must note here that the values in <tt>lab<\/tt> aren't the normal mathematical quantities L*, a*, and b*. That's because those real values have been <i>encoded<\/i> (scaled, shifted, and quantized) to fit into unsigned 8-bit integers. So I'll use the function <tt>lab2double<\/tt> to convert these encoded values into the normal mathematical values.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">lab = lab2double(lab);\r\na = lab(:,:,2);\r\nb = lab(:,:,3);<\/pre><p>I claimed above that the color information is in the a* and b* dimensions. Can we look at the a* and b* values in a way that\r\n      lets us start to see the different M&amp;M colors in the original image? I want to do it by constructing a two-dimensional histogram\r\n      of the a* and b* values.\r\n   <\/p>\r\n   <p>There are many different ways you could formulate and implement this in MATLAB. I've picked a method based on the functions\r\n      <tt>interp1<\/tt> and <tt>accumarray<\/tt>.  The basic idea is to convert each a* value into a horizontal array subscript, convert each b* value into a vertical array\r\n      subscript, and then use <tt>accumarray<\/tt> to accumulate values into a histogram.\r\n   <\/p>\r\n   <p>First, let's set up a notion of our histogram bins in the a* and b* dimensions. I'll specify 101 bins according to their centers.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">N = 101;\r\nbin_centers = linspace(-100, 100, N);<\/pre><p>I want to map a* and b* values such that bin centers become subscript values. I found it easy to think about this process\r\n      in terms of the function <tt>interp1<\/tt>, but others might do it differently. I'll use linear interpolation and extrapolate to handle a* and b* values that are completely\r\n      outside the range of the bin centers.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">subscripts = 1:N;\r\nai = interp1(bin_centers, subscripts, a, <span style=\"color: #A020F0\">'linear'<\/span>, <span style=\"color: #A020F0\">'extrap'<\/span>);\r\nbi = interp1(bin_centers, subscripts, b, <span style=\"color: #A020F0\">'linear'<\/span>, <span style=\"color: #A020F0\">'extrap'<\/span>);<\/pre><p>Now I need to round the subscript matrices, <tt>ai<\/tt> and <tt>bi<\/tt>, and I also need to make sure all the subscript values lie between 1 and N.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">ai = round(ai);\r\nbi = round(bi);\r\n\r\nai = max(min(ai, N), 1);\r\nbi = max(min(bi, N), 1);<\/pre><p>Next we let <tt>accumarray<\/tt> do the heavy lifting of counting how many times each pair of subscript values occurs.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">H = accumarray([bi(:), ai(:)], 1, [N N]);<\/pre><p><tt>H<\/tt> is our two-dimensional histogram. Here's my first attempt at visualizing it. The second argument to imshow, [], tells imshow\r\n      to autoscale the range values in <tt>H<\/tt> for display.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">imshow(H, [])<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2010\/two_dim_histogram_02.jpg\"> <p>There are two issues I need to fix: the displayed image is a bit small, and the autoscaling didn't make the histogram detail\r\n      very visible.  Let's manually specify a different display scaling, and let's also ask <tt>imshow<\/tt> to display the image larger than normal.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">figure\r\nimshow(H, [0 1000], <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, 300)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2010\/two_dim_histogram_03.jpg\"> <p>How about axes tickmarks and labels? Can we get the real a* and b* ranges to show up? Sure. We tell imshow the real horizontal\r\n      coordinates corresponding to the left column and right column (the \"xdata\"), as well as the vertical coordinates corresponding\r\n      to the top row and bottom row (the \"ydata\").\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">xdata = [min(bin_centers), max(bin_centers)];\r\nydata = xdata;\r\nimshow(H,[0 1000], <span style=\"color: #A020F0\">'InitialMagnification'<\/span>, 300, <span style=\"color: #A020F0\">'XData'<\/span>, xdata, <span style=\"color: #A020F0\">'YData'<\/span>, ydata)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2010\/two_dim_histogram_04.jpg\"> <p>Oops, still no axes tickmarks and labels. That's because <tt>imshow<\/tt> doesn't display them by default. But we can manually turn them on with the <tt>axis<\/tt> function.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">axis <span style=\"color: #A020F0\">on<\/span>\r\nxlabel(<span style=\"color: #A020F0\">'a*'<\/span>)\r\nylabel(<span style=\"color: #A020F0\">'b*'<\/span>)<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2010\/two_dim_histogram_05.jpg\"> <p>That's our completed two-dimensional histogram and visualization.<\/p>\r\n   <p>I think that next time I'll explore segmenting in the a*-b* plane. But as usual I'm making up these blog posts as I go along,\r\n      so no promises.\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_38f95f4f55b548328b4160ac79222902() {\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='38f95f4f55b548328b4160ac79222902 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 38f95f4f55b548328b4160ac79222902';\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 2010 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-->\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_38f95f4f55b548328b4160ac79222902()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n            the MATLAB code \r\n            <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.11<br><\/p>\r\n<\/div>\r\n<!--\r\n38f95f4f55b548328b4160ac79222902 ##### SOURCE BEGIN #####\r\n%%\r\n% <https:\/\/blogs.mathworks.com\/steve\/2010\/12\/17\/what-color-is-green\/ Last\r\n% week> I showed this picture of M&Ms on my desk, and I used it to raise\r\n% some vague questions about the definition of the color green. Today I'll\r\n% use this picture as an opportunity to demonstrate how to construct and\r\n% visualize a two-dimensional histogram.\r\n\r\nurl = 'https:\/\/blogs.mathworks.com\/images\/steve\/2010\/mms.jpg';\r\nrgb = imread(url);\r\nimshow(rgb)\r\n\r\n%%\r\n% As a preparatory step, I'll first convert the pixels from the sRGB color\r\n% space to the L*a*b* color space. I've written about the L*a*b* color\r\n% before\r\n% (<https:\/\/blogs.mathworks.com\/steve\/2006\/05\/09\/a-lab-based-uniform-color-scale\/\r\n% 09-May-2006>,\r\n% <https:\/\/blogs.mathworks.com\/steve\/2006\/06\/19\/hue-shifts-near-the-l0-axis\/\r\n% 19-Jun-2006>). It does a couple of nice things. First, it separates\r\n% perceived brightness (L*) from color (a* and b*). Second, as a rough\r\n% approximation it is perceptually uniform. That is, Euclidean distances in\r\n% the L*a*b* space correspond roughly uniformly to perceived color\r\n% differences.\r\n%\r\n% Here's how to do the conversion:\r\n\r\nlab = applycform(rgb, makecform('srgb2lab'));\r\n\r\n%%\r\n% But I must note here that the values in |lab| aren't the normal\r\n% mathematical quantities L*, a*, and b*. That's because those real values\r\n% have been _encoded_ (scaled, shifted, and quantized) to fit into unsigned\r\n% 8-bit integers. So I'll use the function |lab2double| to convert these\r\n% encoded values into the normal mathematical values.\r\n\r\nlab = lab2double(lab);\r\na = lab(:,:,2);\r\nb = lab(:,:,3);\r\n\r\n%%\r\n% I claimed above that the color information is in the a* and b*\r\n% dimensions. Can we look at the a* and b* values in a way that lets us\r\n% start to see the different M&M colors in the original image? I want to do\r\n% it by constructing a two-dimensional histogram of the a* and b* values.\r\n%\r\n% There are many different ways you could formulate and implement this in\r\n% MATLAB. I've picked a method based on the functions |interp1| and\r\n% |accumarray|.  The basic idea is to convert each a* value into a\r\n% horizontal array subscript, convert each b* value into a vertical array\r\n% subscript, and then use |accumarray| to accumulate values into a\r\n% histogram.\r\n%\r\n% First, let's set up a notion of our histogram bins in the a* and b*\r\n% dimensions. I'll specify 101 bins according to their centers.\r\n\r\nN = 101;\r\nbin_centers = linspace(-100, 100, N);\r\n\r\n%%\r\n% I want to map a* and b* values such that bin centers become subscript\r\n% values. I found it easy to think about this process in terms of the\r\n% function |interp1|, but others might do it differently. I'll use linear\r\n% interpolation and extrapolate to handle a* and b* values that are\r\n% completely outside the range of the bin centers.\r\n\r\nsubscripts = 1:N;\r\nai = interp1(bin_centers, subscripts, a, 'linear', 'extrap');\r\nbi = interp1(bin_centers, subscripts, b, 'linear', 'extrap');\r\n\r\n%%\r\n% Now I need to round the subscript matrices, |ai| and |bi|, and I also\r\n% need to make sure all the subscript values lie between 1 and N.\r\n\r\nai = round(ai);\r\nbi = round(bi);\r\n\r\nai = max(min(ai, N), 1);\r\nbi = max(min(bi, N), 1);\r\n\r\n%%\r\n% Next we let |accumarray| do the heavy lifting of counting how many times\r\n% each pair of subscript values occurs.\r\n\r\nH = accumarray([bi(:), ai(:)], 1, [N N]);\r\n\r\n%%\r\n% |H| is our two-dimensional histogram. Here's my first attempt at\r\n% visualizing it. The second argument to imshow, [], tells imshow to\r\n% autoscale the range values in |H| for display.\r\n\r\nimshow(H, [])\r\n\r\n%%\r\n% There are two issues I need to fix: the displayed image is a bit small,\r\n% and the autoscaling didn't make the histogram detail very visible.  Let's\r\n% manually specify a different display scaling, and let's also ask |imshow|\r\n% to display the image larger than normal.\r\n\r\nfigure\r\nimshow(H, [0 1000], 'InitialMagnification', 300)\r\n\r\n%%\r\n% How about axes tickmarks and labels? Can we get the real a* and b* ranges\r\n% to show up? Sure. We tell imshow the real horizontal coordinates\r\n% corresponding to the left column and right column (the \"xdata\"), as well\r\n% as the vertical coordinates corresponding to the top row and bottom row\r\n% (the \"ydata\").\r\n\r\nxdata = [min(bin_centers), max(bin_centers)];\r\nydata = xdata;\r\nimshow(H,[0 1000], 'InitialMagnification', 300, 'XData', xdata, 'YData', ydata)\r\n\r\n%%\r\n% Oops, still no axes tickmarks and labels. That's because |imshow| doesn't\r\n% display them by default. But we can manually turn them on with the |axis|\r\n% function.\r\n\r\naxis on\r\nxlabel('a*')\r\nylabel('b*')\r\n\r\n%%\r\n% That's our completed two-dimensional histogram and visualization.\r\n%\r\n% I think that next time I'll explore segmenting in the a*-b* plane. But as\r\n% usual I'm making up these blog posts as I go along, so no promises.\r\n##### SOURCE END ##### 38f95f4f55b548328b4160ac79222902\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   Last week I showed this picture of M&amp;Ms on my desk, and I used it to raise some vague questions about the definition of the color green.\r\n      Today I'll use this picture as an opportunity... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2010\/12\/23\/two-dimensional-histograms\/\">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":[336,114,50,725,76,36,723,721,32,112,122,380,188,94,96],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/356"}],"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=356"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/356\/revisions"}],"predecessor-version":[{"id":3711,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/356\/revisions\/3711"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=356"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=356"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=356"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}