{"id":2701,"date":"2017-09-29T16:23:01","date_gmt":"2017-09-29T20:23:01","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=2701"},"modified":"2019-11-01T17:17:21","modified_gmt":"2019-11-01T21:17:21","slug":"feret-diameter-introduction","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2017\/09\/29\/feret-diameter-introduction\/","title":{"rendered":"Feret Diameter: Introduction"},"content":{"rendered":"<div class=\"content\"><p>This is the first of a few blog posts about object measurements based on a concept called the <i>Feret diameter<\/i>, sometimes called the <i>caliper diameter<\/i>. The diagram below illustrates the concept. Place the object to be measured inside the jaws of a caliper, with the caliper oriented at a specified angle. Close the jaws tightly on the object while maintaining that angle. The distance between the jaws is the Feret diameter at that angle.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/caliper-dimensions.png\" alt=\"\"> <\/p><p>Eventually, we'll examine several related measurements (and algorithms for computing them), like these.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/sample-vis.png\" alt=\"\"> <\/p><p>Today, though, I'm just going to play around with a very small binary image. (Note: the <tt>pixelgrid<\/tt> function is a utility that I wrote. It's at the bottom of this post.)<\/p><pre class=\"codeinput\">bw = [ <span class=\"keyword\">...<\/span>\r\n    0   0   0   0   0   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   1   0\r\n    0   0   0   0   0   0   1   1   1   1   0\r\n    0   0   0   0   0   1   1   1   1   0   0\r\n    0   0   0   1   1   1   1   1   1   0   0\r\n    0   0   1   1   1   0   0   0   0   0   0\r\n    0   0   1   1   1   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   0   0 ];\r\n\r\n\r\nimshow(bw)\r\npixelgrid\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/feret_diameter_1_01.png\" alt=\"\"> <p>I want to brute-force my to finding the two corners of the white blob that are farthest apart. Let's start by finding all the pixel corners. (I'll be using <a href=\"https:\/\/blogs.mathworks.com\/loren\/2016\/10\/24\/matlab-arithmetic-expands-in-r2016b\/\">implicit expansion<\/a> here, which works in R2016b or later.)<\/p><pre class=\"codeinput\">[y,x] = find(bw);\r\nhold <span class=\"string\">on<\/span>\r\nplot(x,y,<span class=\"string\">'xb'<\/span>,<span class=\"string\">'MarkerSize'<\/span>,5)\r\ncorners = [x y] + cat(3,[.5 .5],[.5 -.5],[-.5 .5],[-.5 -.5]);\r\ncorners = permute(corners,[1 3 2]);\r\ncorners = reshape(corners,[],2);\r\ncorners = unique(corners,<span class=\"string\">'rows'<\/span>);\r\nplot(corners(:,1),corners(:,2),<span class=\"string\">'sr'<\/span>,<span class=\"string\">'MarkerSize'<\/span>,5)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/feret_diameter_1_02.png\" alt=\"\"> <p>Well, let's get away from pure brute force just a little. We really only need to look at the points that are part of the <i>convex hull<\/i> of all the corner points.<\/p><pre class=\"codeinput\">hold <span class=\"string\">on<\/span>\r\nk = convhull(corners);\r\nhull_corners = corners(k,:);\r\nplot(hull_corners(:,1),hull_corners(:,2),<span class=\"string\">'r'<\/span>,<span class=\"string\">'LineWidth'<\/span>,3)\r\nplot(hull_corners(:,1),hull_corners(:,2),<span class=\"string\">'ro'<\/span>,<span class=\"string\">'MarkerSize'<\/span>,10,<span class=\"string\">'MarkerFaceColor'<\/span>,<span class=\"string\">'r'<\/span>)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/feret_diameter_1_03.png\" alt=\"\"> <p>Now let's find which two points on the convex hull are farthest apart.<\/p><pre class=\"codeinput\">dx = hull_corners(:,1) - hull_corners(:,1)';\r\ndy = hull_corners(:,2) - hull_corners(:,2)';\r\npairwise_dist = hypot(dx,dy);\r\n[max_dist,j] = max(pairwise_dist(:));\r\n[k1,k2] = ind2sub(size(pairwise_dist),j);\r\n\r\npoint1 = hull_corners(k1,:);\r\npoint2 = hull_corners(k2,:);\r\nhold <span class=\"string\">on<\/span>\r\nplot([point1(1) point2(1)],[point1(2) point2(2)],<span class=\"string\">'-db'<\/span>,<span class=\"string\">'LineWidth'<\/span>,3,<span class=\"string\">'MarkerFaceColor'<\/span>,<span class=\"string\">'b'<\/span>)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/feret_diameter_1_04.png\" alt=\"\"> <p>And the maximum distance is ...<\/p><pre class=\"codeinput\">max_dist\r\n<\/pre><pre class=\"codeoutput\">\r\nmax_dist =\r\n\r\n    10\r\n\r\n<\/pre><p>(I thought I had made a mistake when I first saw this number come out to be an exact integer. But no, the answer is really 10.)<\/p><p>That's a pretty good start. I don't know exactly where I'll go with this topic next time. I'm making it up as I go.<\/p><pre class=\"codeinput\"><span class=\"keyword\">function<\/span> hout = pixelgrid(h)\r\n<span class=\"comment\">%pixelgrid Superimpose a grid of pixel edges on an image<\/span>\r\n<span class=\"comment\">%   pixelgrid superimposes a grid of pixel edges on the image in the<\/span>\r\n<span class=\"comment\">%   current axes.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   pixelgrid(h_ax) superimposes the grid on the image in the specified<\/span>\r\n<span class=\"comment\">%   axes.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   pixelgrid(h_im) superimposes the grid on the specified image.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   group = pixelgrid(___) returns an hggroup object that contains the two<\/span>\r\n<span class=\"comment\">%   lines that are used to draw the grid. One line is thick and darker, and<\/span>\r\n<span class=\"comment\">%   the other is thin and lighter. Using two contrasting lines in this way<\/span>\r\n<span class=\"comment\">%   guarantees that the grid will be visible regardless of pixel colors.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   EXAMPLES<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   Superimpose pixel grid on color image. Zoom in.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%       rgb = imread('peppers.png');<\/span>\r\n<span class=\"comment\">%       imshow(rgb)<\/span>\r\n<span class=\"comment\">%       pixelgrid<\/span>\r\n<span class=\"comment\">%       axis([440 455 240 250])<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   Change the colors and line widths of the pixel grid.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%       rgb = imread('peppers.png');<\/span>\r\n<span class=\"comment\">%       imshow(rgb)<\/span>\r\n<span class=\"comment\">%       h = pixelgrid;<\/span>\r\n<span class=\"comment\">%       axis([440 455 240 250])<\/span>\r\n<span class=\"comment\">%       h.Children(1).Color = [178 223 138]\/255;<\/span>\r\n<span class=\"comment\">%       h.Children(1).LineWidth = 2;<\/span>\r\n<span class=\"comment\">%       h.Children(2).Color = [31 120 180]\/255;<\/span>\r\n<span class=\"comment\">%       h.Children(2).LineWidth = 4;<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   LIMITATIONS<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   This function is intended for use when looking at a zoomed-in image<\/span>\r\n<span class=\"comment\">%   region with a relatively small number of rows and columns. If you use<\/span>\r\n<span class=\"comment\">%   this function on a typical, full-size image without zooming in, the<\/span>\r\n<span class=\"comment\">%   image will not be visible under the grid lines.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">%   This function attempts to determine if MATLAB is running with a high<\/span>\r\n<span class=\"comment\">%   DPI display. If so, then it displays the pixel grid using thinner<\/span>\r\n<span class=\"comment\">%   lines. However, the method for detecting a high DPI display on works on<\/span>\r\n<span class=\"comment\">%   a Mac when Java AWT is available.<\/span>\r\n\r\n<span class=\"comment\">%   Steve Eddins<\/span>\r\n<span class=\"comment\">%   Copyright The MathWorks, Inc. 2017<\/span>\r\n\r\n<span class=\"keyword\">if<\/span> nargin &lt; 1\r\n    him = findobj(gca,<span class=\"string\">'type'<\/span>,<span class=\"string\">'image'<\/span>);\r\n<span class=\"keyword\">elseif<\/span> strcmp(h.Type,<span class=\"string\">'axes'<\/span>)\r\n    him = findobj(h,<span class=\"string\">'type'<\/span>,<span class=\"string\">'image'<\/span>);\r\n<span class=\"keyword\">elseif<\/span> strcmp(h.Type,<span class=\"string\">'image'<\/span>)\r\n    him = h;\r\n<span class=\"keyword\">else<\/span>\r\n    error(<span class=\"string\">'Invalid graphics object.'<\/span>)\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">if<\/span> isempty(him)\r\n    error(<span class=\"string\">'Image not found.'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\nhax = ancestor(him,<span class=\"string\">'axes'<\/span>);\r\n\r\nxdata = him.XData;\r\nydata = him.YData;\r\n[M,N,~] = size(him.CData);\r\n\r\n<span class=\"keyword\">if<\/span> M &gt; 1\r\n    pixel_height = diff(ydata) \/ (M-1);\r\n<span class=\"keyword\">else<\/span>\r\n    <span class=\"comment\">% Special case. Assume unit height.<\/span>\r\n    pixel_height = 1;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">if<\/span> N &gt; 1\r\n    pixel_width = diff(xdata) \/ (N-1);\r\n<span class=\"keyword\">else<\/span>\r\n    <span class=\"comment\">% Special case. Assume unit width.<\/span>\r\n    pixel_width = 1;\r\n<span class=\"keyword\">end<\/span>\r\n\r\ny_top = ydata(1) - (pixel_height\/2);\r\ny_bottom = ydata(2) + (pixel_height\/2);\r\ny = linspace(y_top, y_bottom, M+1);\r\n\r\nx_left = xdata(1) - (pixel_width\/2);\r\nx_right = xdata(2) + (pixel_width\/2);\r\nx = linspace(x_left, x_right, N+1);\r\n\r\n<span class=\"comment\">% Construct xv1 and yv1 to draw all the vertical line segments. Separate<\/span>\r\n<span class=\"comment\">% the line segments by NaN to avoid drawing diagonal line segments from the<\/span>\r\n<span class=\"comment\">% bottom of one line to the top of the next line over.<\/span>\r\nxv1 = NaN(1,3*numel(x));\r\nxv1(1:3:end) = x;\r\nxv1(2:3:end) = x;\r\nyv1 = repmat([y(1) y(end) NaN], 1, numel(x));\r\n\r\n<span class=\"comment\">% Construct xv2 and yv2 to draw all the horizontal line segments.<\/span>\r\nyv2 = NaN(1,3*numel(y));\r\nyv2(1:3:end) = y;\r\nyv2(2:3:end) = y;\r\nxv2 = repmat([x(1) x(end) NaN], 1, numel(y));\r\n\r\n<span class=\"comment\">% Put all the vertices together so that they can be drawn with a single<\/span>\r\n<span class=\"comment\">% call to line().<\/span>\r\nxv = [xv1(:) ; xv2(:)];\r\nyv = [yv1(:) ; yv2(:)];\r\n\r\nhh = hggroup(hax);\r\ndark_gray = [0.3 0.3 0.3];\r\nlight_gray = [0.8 0.8 0.8];\r\n\r\n<span class=\"keyword\">if<\/span> isHighDPI\r\n    bottom_line_width = 1.5;\r\n    top_line_width = 0.5;\r\n<span class=\"keyword\">else<\/span>\r\n    bottom_line_width = 3;\r\n    top_line_width = 1;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"comment\">% When creating the lines, use AlignVertexCenters to avoid antialias<\/span>\r\n<span class=\"comment\">% effects that would cause some lines in the grid to appear brighter than<\/span>\r\n<span class=\"comment\">% others.<\/span>\r\nline(<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Parent'<\/span>,hh,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'XData'<\/span>,xv,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'YData'<\/span>,yv,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'LineWidth'<\/span>,bottom_line_width,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Color'<\/span>,dark_gray,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'LineStyle'<\/span>,<span class=\"string\">'-'<\/span>,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'AlignVertexCenters'<\/span>,<span class=\"string\">'on'<\/span>);\r\nline(<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Parent'<\/span>,hh,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'XData'<\/span>,xv,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'YData'<\/span>,yv,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'LineWidth'<\/span>,top_line_width,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Color'<\/span>,light_gray,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'LineStyle'<\/span>,<span class=\"string\">'-'<\/span>,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'AlignVertexCenters'<\/span>,<span class=\"string\">'on'<\/span>);\r\n\r\n<span class=\"comment\">% Only return an output if requested.<\/span>\r\n<span class=\"keyword\">if<\/span> nargout &gt; 0\r\n    hout = hh;\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> tf = isHighDPI\r\n<span class=\"comment\">% Returns true if running with a high DPI default display.<\/span>\r\n<span class=\"comment\">%<\/span>\r\n<span class=\"comment\">% LIMITATION: Only works on a Mac with Java AWT available. In other<\/span>\r\n<span class=\"comment\">% situations, this function always returns false.<\/span>\r\ntf = false;\r\n<span class=\"keyword\">if<\/span> ismac\r\n    <span class=\"keyword\">if<\/span> usejava(<span class=\"string\">'awt'<\/span>)\r\n        sf = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice.getScaleFactor();\r\n        tf = sf &gt; 1;\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><script language=\"JavaScript\"> <!-- \r\n    function grabCode_7426ac4b32424f798cb7fb50b402a56d() {\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='7426ac4b32424f798cb7fb50b402a56d ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 7426ac4b32424f798cb7fb50b402a56d';\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_7426ac4b32424f798cb7fb50b402a56d()\"><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; R2017b<br><\/p><\/div><!--\r\n7426ac4b32424f798cb7fb50b402a56d ##### SOURCE BEGIN #####\r\n%%\r\n% This is the first of a few blog posts about object measurements based on\r\n% a concept called the _Feret diameter_, sometimes called the _caliper\r\n% diameter_. The diagram below illustrates the concept. Place the object to\r\n% be measured inside the jaws of a caliper, with the caliper oriented at a\r\n% specified angle. Close the jaws tightly on the object while maintaining\r\n% that angle. The distance between the jaws is the Feret diameter at that\r\n% angle.\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/steve\/files\/caliper-dimensions.png>>\r\n%\r\n% Eventually, we'll examine several related measurements (and algorithms for\r\n% computing them), like these.\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/steve\/files\/sample-vis.png>>\r\n%\r\n% Today, though, I'm just going to play around with a very small binary\r\n% image. (Note: the |pixelgrid| function is a utility that I wrote. It's at\r\n% the bottom of this post.) \r\n\r\nbw = [ ...\r\n    0   0   0   0   0   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   1   0\r\n    0   0   0   0   0   0   1   1   1   1   0\r\n    0   0   0   0   0   1   1   1   1   0   0\r\n    0   0   0   1   1   1   1   1   1   0   0\r\n    0   0   1   1   1   0   0   0   0   0   0\r\n    0   0   1   1   1   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   0   0\r\n    0   0   0   0   0   0   0   0   0   0   0 ];\r\n\r\n\r\nimshow(bw)\r\npixelgrid\r\n\r\n%%\r\n% I want to brute-force my to finding the two\r\n% corners of the white blob that are farthest apart. Let's start by finding\r\n% all the pixel corners. (I'll be using\r\n% <https:\/\/blogs.mathworks.com\/loren\/2016\/10\/24\/matlab-arithmetic-expands-in-r2016b\/\r\n% implicit expansion> here, which works in R2016b or later.)\r\n\r\n[y,x] = find(bw);\r\nhold on\r\nplot(x,y,'xb','MarkerSize',5)\r\ncorners = [x y] + cat(3,[.5 .5],[.5 -.5],[-.5 .5],[-.5 -.5]);\r\ncorners = permute(corners,[1 3 2]);\r\ncorners = reshape(corners,[],2);\r\ncorners = unique(corners,'rows');\r\nplot(corners(:,1),corners(:,2),'sr','MarkerSize',5)\r\nhold off\r\n\r\n%%\r\n% Well, let's get away from pure brute force just a little. We really only\r\n% need to look at the points that are part of the _convex hull_ of all the\r\n% corner points.\r\n\r\nhold on\r\nk = convhull(corners);\r\nhull_corners = corners(k,:);\r\nplot(hull_corners(:,1),hull_corners(:,2),'r','LineWidth',3)\r\nplot(hull_corners(:,1),hull_corners(:,2),'ro','MarkerSize',10,'MarkerFaceColor','r')\r\nhold off\r\n\r\n%%\r\n% Now let's find which two points on the convex hull are farthest apart.\r\n\r\ndx = hull_corners(:,1) - hull_corners(:,1)';\r\ndy = hull_corners(:,2) - hull_corners(:,2)';\r\npairwise_dist = hypot(dx,dy);\r\n[max_dist,j] = max(pairwise_dist(:));\r\n[k1,k2] = ind2sub(size(pairwise_dist),j);\r\n\r\npoint1 = hull_corners(k1,:);\r\npoint2 = hull_corners(k2,:);\r\nhold on\r\nplot([point1(1) point2(1)],[point1(2) point2(2)],'-db','LineWidth',3,'MarkerFaceColor','b')\r\nhold off\r\n\r\n%%\r\n% And the maximum distance is ...\r\n\r\nmax_dist\r\n\r\n%%\r\n% (I thought I had made a mistake when I first saw this number come out to\r\n% be an exact integer. But no, the answer is really 10.)\r\n%\r\n% That's a pretty good start. I don't know exactly where I'll go with this\r\n% topic next time. I'm making it up as I go.\r\n\r\n%%\r\n\r\nfunction hout = pixelgrid(h)\r\n%pixelgrid Superimpose a grid of pixel edges on an image\r\n%   pixelgrid superimposes a grid of pixel edges on the image in the\r\n%   current axes.\r\n%\r\n%   pixelgrid(h_ax) superimposes the grid on the image in the specified\r\n%   axes.\r\n%\r\n%   pixelgrid(h_im) superimposes the grid on the specified image.\r\n%\r\n%   group = pixelgrid(___) returns an hggroup object that contains the two\r\n%   lines that are used to draw the grid. One line is thick and darker, and\r\n%   the other is thin and lighter. Using two contrasting lines in this way\r\n%   guarantees that the grid will be visible regardless of pixel colors.\r\n%\r\n%   EXAMPLES\r\n%\r\n%   Superimpose pixel grid on color image. Zoom in.\r\n%\r\n%       rgb = imread('peppers.png');\r\n%       imshow(rgb)\r\n%       pixelgrid\r\n%       axis([440 455 240 250])\r\n%\r\n%   Change the colors and line widths of the pixel grid.\r\n%\r\n%       rgb = imread('peppers.png');\r\n%       imshow(rgb)\r\n%       h = pixelgrid;\r\n%       axis([440 455 240 250])\r\n%       h.Children(1).Color = [178 223 138]\/255;\r\n%       h.Children(1).LineWidth = 2;\r\n%       h.Children(2).Color = [31 120 180]\/255;\r\n%       h.Children(2).LineWidth = 4;\r\n%\r\n%   LIMITATIONS\r\n%\r\n%   This function is intended for use when looking at a zoomed-in image\r\n%   region with a relatively small number of rows and columns. If you use\r\n%   this function on a typical, full-size image without zooming in, the\r\n%   image will not be visible under the grid lines.\r\n%\r\n%   This function attempts to determine if MATLAB is running with a high\r\n%   DPI display. If so, then it displays the pixel grid using thinner\r\n%   lines. However, the method for detecting a high DPI display on works on\r\n%   a Mac when Java AWT is available.\r\n\r\n%   Steve Eddins\r\n%   Copyright The MathWorks, Inc. 2017\r\n\r\nif nargin < 1\r\n    him = findobj(gca,'type','image');\r\nelseif strcmp(h.Type,'axes')\r\n    him = findobj(h,'type','image');\r\nelseif strcmp(h.Type,'image')\r\n    him = h;\r\nelse\r\n    error('Invalid graphics object.')\r\nend\r\n\r\nif isempty(him)\r\n    error('Image not found.');\r\nend\r\n\r\nhax = ancestor(him,'axes');\r\n\r\nxdata = him.XData;\r\nydata = him.YData;\r\n[M,N,~] = size(him.CData);\r\n\r\nif M > 1\r\n    pixel_height = diff(ydata) \/ (M-1);\r\nelse\r\n    % Special case. Assume unit height.\r\n    pixel_height = 1;\r\nend\r\n\r\nif N > 1\r\n    pixel_width = diff(xdata) \/ (N-1);\r\nelse\r\n    % Special case. Assume unit width.\r\n    pixel_width = 1;\r\nend\r\n\r\ny_top = ydata(1) - (pixel_height\/2);\r\ny_bottom = ydata(2) + (pixel_height\/2);\r\ny = linspace(y_top, y_bottom, M+1);\r\n\r\nx_left = xdata(1) - (pixel_width\/2);\r\nx_right = xdata(2) + (pixel_width\/2);\r\nx = linspace(x_left, x_right, N+1);\r\n\r\n% Construct xv1 and yv1 to draw all the vertical line segments. Separate\r\n% the line segments by NaN to avoid drawing diagonal line segments from the\r\n% bottom of one line to the top of the next line over.\r\nxv1 = NaN(1,3*numel(x));\r\nxv1(1:3:end) = x;\r\nxv1(2:3:end) = x;\r\nyv1 = repmat([y(1) y(end) NaN], 1, numel(x));\r\n\r\n% Construct xv2 and yv2 to draw all the horizontal line segments.\r\nyv2 = NaN(1,3*numel(y));\r\nyv2(1:3:end) = y;\r\nyv2(2:3:end) = y;\r\nxv2 = repmat([x(1) x(end) NaN], 1, numel(y));\r\n\r\n% Put all the vertices together so that they can be drawn with a single\r\n% call to line().\r\nxv = [xv1(:) ; xv2(:)];\r\nyv = [yv1(:) ; yv2(:)];\r\n\r\nhh = hggroup(hax);\r\ndark_gray = [0.3 0.3 0.3];\r\nlight_gray = [0.8 0.8 0.8];\r\n\r\nif isHighDPI\r\n    bottom_line_width = 1.5;\r\n    top_line_width = 0.5;\r\nelse\r\n    bottom_line_width = 3;\r\n    top_line_width = 1;\r\nend\r\n\r\n% When creating the lines, use AlignVertexCenters to avoid antialias\r\n% effects that would cause some lines in the grid to appear brighter than\r\n% others.\r\nline(...\r\n    'Parent',hh,...\r\n    'XData',xv,...\r\n    'YData',yv,...\r\n    'LineWidth',bottom_line_width,...\r\n    'Color',dark_gray,...\r\n    'LineStyle','-',...\r\n    'AlignVertexCenters','on');\r\nline(...\r\n    'Parent',hh,...\r\n    'XData',xv,...\r\n    'YData',yv,...\r\n    'LineWidth',top_line_width,...\r\n    'Color',light_gray,...\r\n    'LineStyle','-',...\r\n    'AlignVertexCenters','on');\r\n\r\n% Only return an output if requested.\r\nif nargout > 0\r\n    hout = hh;\r\nend\r\n\r\nend\r\n\r\nfunction tf = isHighDPI\r\n% Returns true if running with a high DPI default display.\r\n%\r\n% LIMITATION: Only works on a Mac with Java AWT available. In other\r\n% situations, this function always returns false.\r\ntf = false;\r\nif ismac\r\n    if usejava('awt')\r\n        sf = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice.getScaleFactor();\r\n        tf = sf > 1;\r\n    end\r\nend\r\nend\r\n\r\n\r\n##### SOURCE END ##### 7426ac4b32424f798cb7fb50b402a56d\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/steve\/files\/feret_diameter_1_04.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>This is the first of a few blog posts about object measurements based on a concept called the Feret diameter, sometimes called the caliper diameter. The diagram below illustrates the concept. Place... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2017\/09\/29\/feret-diameter-introduction\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":2706,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[272,835,695,1199,348,250,589,90,334,36,470,472,1203,747,32,122,575,1201,1195,68,116,170,190,1197,350,1205],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2701"}],"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=2701"}],"version-history":[{"count":2,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2701\/revisions"}],"predecessor-version":[{"id":2707,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/2701\/revisions\/2707"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media\/2706"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=2701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=2701"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=2701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}