{"id":173,"date":"2009-02-24T15:06:19","date_gmt":"2009-02-24T15:06:19","guid":{"rendered":"https:\/\/blogs.mathworks.com\/loren\/2009\/02\/24\/decomposing-embedded-images\/"},"modified":"2009-02-17T19:24:31","modified_gmt":"2009-02-17T19:24:31","slug":"decomposing-embedded-images","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/loren\/2009\/02\/24\/decomposing-embedded-images\/","title":{"rendered":"Decomposing Embedded Images"},"content":{"rendered":"<div xmlns:mwsh=\"https:\/\/www.mathworks.com\/namespace\/mcode\/v1\/syntaxhighlight.dtd\" class=\"content\">\r\n   <introduction>\r\n      <p>Today I&#8217;d like to introduce a guest blogger, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/15007\">Jiro<\/a>, who is an application engineer here at The MathWorks. Some of you may know him as one of the bloggers for the <a href=\"https:\/\/blogs.mathworks.com\/pick\/\">File Exchange Pick of the Week<\/a>.\r\n      <\/p>\r\n   <\/introduction>\r\n   <h3>Contents<\/h3>\r\n   <div>\r\n      <ul>\r\n         <li><a href=\"#2\">Setup<\/a><\/li>\r\n         <li><a href=\"#3\">Show Images<\/a><\/li>\r\n         <li><a href=\"#5\">Image Decomposition<\/a><\/li>\r\n         <li><a href=\"#13\">Decomposing the Second Image<\/a><\/li>\r\n         <li><a href=\"#14\">How It Works<\/a><\/li>\r\n         <li><a href=\"#20\">Embed Function &amp; Decode Function<\/a><\/li>\r\n         <li><a href=\"#22\">Cleanup<\/a><\/li>\r\n         <li><a href=\"#23\">Comments?<\/a><\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <p>I recently presented to a group of freshman engineering students at Virginia Commonwealth University. I wanted to show something\r\n      that was fun, eye-catching, and relatively easy to explain. So I created this image-related demo. I am not an expert in image\r\n      processing, but I certainly had a lot of fun working on this example. For anyone interested in hardcore image processing,\r\n      I would suggest also taking a look at <a href=\"https:\/\/blogs.mathworks.com\/steve\/\">Steve's Image Processing blog<\/a>.\r\n   <\/p>\r\n   <p>This example demonstrates how to embed an image into another image and how to decompose the images. The embedded image is\r\n      created by storing two 8-bit RGB image as 16-bit RGB image. The primary image is stored in the most significant byte, while\r\n      the secondary image is stored in the least significant byte. (See the section titled <b>\"Embed Function &amp; Decode Function\"<\/b> of this post for the code). With this method, the secondary image can be concealed inside the primary image, without any\r\n      loss of information.\r\n   <\/p>\r\n   <p>This post focuses on the decomposition part of the demo.<\/p>\r\n   <p><i>This demo uses functions from the <a href=\"https:\/\/www.mathworks.com\/products\/image\/\">Image Processing Toolbox<\/a>.<\/i><\/p>\r\n   <h3>Setup<a name=\"2\"><\/a><\/h3><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">curImshowBorder = iptgetpref(<span style=\"color: #A020F0\">'ImshowBorder'<\/span>);\r\niptsetpref(<span style=\"color: #A020F0\">'ImshowBorder'<\/span>, <span style=\"color: #A020F0\">'tight'<\/span>);<\/pre><h3>Show Images<a name=\"3\"><\/a><\/h3>\r\n   <p>Here are two images that look the same. But are they??<\/p>\r\n   <div>\r\n      <ul>\r\n         <li>peppers_BlueHills.png<\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/peppers_BlueHills.png\"> <\/p>\r\n   <div>\r\n      <ul>\r\n         <li>peppers_trees.png<\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/peppers_trees.png\"> <\/p>\r\n   <p>Would you believe me if I say these were very different images? Perhaps you'll believe MATLAB:<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">isequal(imread(<span style=\"color: #A020F0\">'peppers_BlueHills.png'<\/span>), imread(<span style=\"color: #A020F0\">'peppers_trees.png'<\/span>))<\/pre><pre style=\"font-style:oblique\">ans =\r\n     0\r\n<\/pre><p>In fact, they have completely different images embedded in them. You just can't tell with the naked eye.<\/p>\r\n   <h3>Image Decomposition<a name=\"5\"><\/a><\/h3>\r\n   <p>Let's try to decompose the image and see what's hidden inside.<\/p>\r\n   <p>These are 16-bit RGB images. See the <b>\"Embed Function &amp; Decode Function\"<\/b> section of this report to see the function that I used for creating these images.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">imData = imread(<span style=\"color: #A020F0\">'peppers_BlueHills.png'<\/span>);\r\nwhos <span style=\"color: #A020F0\">imData<\/span><\/pre><pre style=\"font-style:oblique\">  Name          Size                 Bytes  Class     Attributes\r\n\r\n  imData      384x512x3            1179648  uint16              \r\n\r\n<\/pre><p>The image contains two 8-bit images. The primary image is stored in the most significant byte and the secondary image is stored\r\n      in the least significant byte.\r\n   <\/p>\r\n   <p><b>Convert RGB 3-D Array to a Vector<\/b><\/p>\r\n   <p>We'll be using <tt>TYPECAST<\/tt> to convert the data type, and the function requires a vector.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixelVals = imData(:);\r\npixelVals(1:10)<\/pre><pre style=\"font-style:oblique\">ans =\r\n  16097\r\n  16352\r\n  16862\r\n  16348\r\n  16346\r\n  16344\r\n  16087\r\n  16854\r\n  16082\r\n  15822\r\n<\/pre><p><b>Convert UINT16 to UINT8<\/b><\/p>\r\n   <p>Next, convert the data type from <tt>UINT16<\/tt> to <tt>UINT8<\/tt>. In doing so, we'll use <tt>TYPECAST<\/tt> (instead of <tt>CAST<\/tt>) to preserve the data.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixelValsConv = typecast(pixelVals, <span style=\"color: #A020F0\">'uint8'<\/span>);\r\nwhos <span style=\"color: #A020F0\">pixelVals*<\/span><\/pre><pre style=\"font-style:oblique\">  Name                     Size              Bytes  Class     Attributes\r\n\r\n  pixelVals           589824x1             1179648  uint16              \r\n  pixelValsConv      1179648x1             1179648  uint8               \r\n\r\n<\/pre><p>Notice that <tt>pixelValsConv<\/tt> has twice as many elements. This is because there are two 8-bit values to a 16-bit value.\r\n   <\/p>\r\n   <p><b>Separate Two Images<\/b><\/p>\r\n   <p>We'll reshape them to separate out the least and the most significant bytes.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixelValsConv = reshape(pixelValsConv, 2, [])';\r\npixelValsConv(1:10, :)<\/pre><pre style=\"font-style:oblique\">ans =\r\n  225   62\r\n  224   63\r\n  222   65\r\n  220   63\r\n  218   63\r\n  216   63\r\n  215   62\r\n  214   65\r\n  210   62\r\n  206   61\r\n<\/pre><p>On a system with \"little-endian\" architecture, the first column is the least significant byte and the second column is the\r\n      most significant column.\r\n   <\/p><pre> (first pixel)  62*256 + 225 = 16097<\/pre><p>On a \"big-endian\" architecture system, the order is switched.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">[cmp,maxsize,endian] = computer\r\n\r\n<span style=\"color: #0000FF\">if<\/span> strcmp(endian, <span style=\"color: #A020F0\">'L'<\/span>)\r\n  imOrder = [2 1];\r\n<span style=\"color: #0000FF\">else<\/span>\r\n  imOrder = [1 2];\r\n<span style=\"color: #0000FF\">end<\/span><\/pre><pre style=\"font-style:oblique\">cmp =\r\nPCWIN\r\nmaxsize =\r\n  2.1475e+009\r\nendian =\r\nL\r\n<\/pre><p>We'll take each column and reshape them as the primary and secondary images.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">imDataPrimary   = reshape(pixelValsConv(:, imOrder(1)), size(imData));\r\nimDataSecondary = reshape(pixelValsConv(:, imOrder(2)), size(imData));<\/pre><p>We can see that we end up with two images, both of which are now <tt>UINT8<\/tt> images.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">whos <span style=\"color: #A020F0\">imData*<\/span><\/pre><pre style=\"font-style:oblique\">  Name                    Size                 Bytes  Class     Attributes\r\n\r\n  imData                384x512x3            1179648  uint16              \r\n  imData2Primary        384x512x3             589824  uint8               \r\n  imData2Secondary      384x512x3             589824  uint8               \r\n  imDataPrimary         384x512x3             589824  uint8               \r\n  imDataSecondary       384x512x3             589824  uint8               \r\n\r\n<\/pre><p><b>Show Primary and Secondary Images<\/b><\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">figure;imshow(imDataPrimary);\r\nfigure;imshow(imDataSecondary);<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_01.png\"> <img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_02.png\"> <h3>Decomposing the Second Image<a name=\"13\"><\/a><\/h3>\r\n   <p>We'll do the same thing for the second image we read in. Let's see what's embedded in that one. We'll use <a href=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/decodeImage.m\"><tt>decodeImage.m<\/tt><\/a> which is a function with the above algorithm.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">[imData2Primary, imData2Secondary] = decodeImage(<span style=\"color: #A020F0\">'peppers_trees.png'<\/span>);\r\nfigure; imshow(imData2Primary);\r\nfigure; imshow(imData2Secondary);<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_03.png\"> <img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_04.png\"> <h3>How It Works<a name=\"14\"><\/a><\/h3>\r\n   <p>So, how does this work? Why is the secondary image unrecognizable by the naked eye? That's because the secondary image is\r\n      stored in the least significant byte.\r\n   <\/p>\r\n   <p>To understand this, let's take a look at a single row of pixels in one of the RGB planes. We'll look at row 150 of the red\r\n      plane.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixelRow16 = imData(150, :, 1);  <span style=\"color: #228B22\">% row 150, red plane<\/span><\/pre><p>We covert this vector to <tt>UINT8<\/tt> using <tt>TYPECAST<\/tt>:\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">pixelRow8 = typecast(pixelRow16, <span style=\"color: #A020F0\">'uint8'<\/span>);\r\npixelRow8 = reshape(pixelRow8, 2, []);\r\npixelRow8(:, 1:10)<\/pre><pre style=\"font-style:oblique\">ans =\r\n   64   60   61   61   57   60   61   61   61   61\r\n   67   70   71   70   70   72   74   69   63   63\r\n<\/pre><p>On a little-endian architecture system, the first row is the secondary image, and the second row is the primary image. Next,\r\n      we'll create a <tt>UINT16<\/tt> vector with only the primary image.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\"><span style=\"color: #228B22\">% set the secondary image vector to ZERO<\/span>\r\npixelRow8Main  = [zeros(1, size(pixelRow8, 2), <span style=\"color: #A020F0\">'uint8'<\/span>); pixelRow8(2, :)];\r\npixelRow16Main = typecast(pixelRow8Main(:)', <span style=\"color: #A020F0\">'uint16'<\/span>);<\/pre><p>Now, let's compare the values of the total image vector with those of the primary image vector.<\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">figure;\r\nax1 = axes;hold <span style=\"color: #A020F0\">on<\/span>;\r\nplot(pixelRow16);\r\nplot(pixelRow16Main, <span style=\"color: #A020F0\">'r'<\/span>);\r\nxlabel(<span style=\"color: #A020F0\">'Pixel Count'<\/span>); ylabel(<span style=\"color: #A020F0\">'Pixel Value (UINT16)'<\/span>);\r\nlegend(<span style=\"color: #A020F0\">'Total Image'<\/span>, <span style=\"color: #A020F0\">'Primary Image'<\/span>, <span style=\"color: #A020F0\">'Location'<\/span>, <span style=\"color: #A020F0\">'NorthWest'<\/span>);\r\nrectX = 160; rectY = 15000; rectW = 30; rectH = 3500;\r\nrectangle(<span style=\"color: #A020F0\">'Position'<\/span>, [rectX, rectY, rectW, rectH]);\r\ndar = get(ax1, <span style=\"color: #A020F0\">'DataAspectRatio'<\/span>); dar = dar(2)\/dar(1);\r\nw = .3; h = w*(rectH\/rectW)\/dar;\r\nax2 = axes(<span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'Units'<\/span>, <span style=\"color: #A020F0\">'Normalized'<\/span>, <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'Position'<\/span>, [.6 .2 w h], <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'Box'<\/span>, <span style=\"color: #A020F0\">'on'<\/span>, <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'LineWidth'<\/span>, 2, <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'XTick'<\/span>, [], <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'Ytick'<\/span>, [], <span style=\"color: #0000FF\">...<\/span>\r\n  <span style=\"color: #A020F0\">'Color'<\/span>, [.95 .95 .95]);hold <span style=\"color: #A020F0\">on<\/span>;\r\nxlabel(<span style=\"color: #A020F0\">'Magnified Region'<\/span>);\r\nplot(pixelRow16);\r\nplot(pixelRow16Main, <span style=\"color: #A020F0\">'r'<\/span>);\r\nxlim([rectX, rectX+rectW]);\r\nylim([rectY, rectY+rectH]);<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_05.png\"> <p>As this figure shows, the primary image represents the majority of the information. The information for the secondary image\r\n      is much smaller relative to the primary image. But if we look at the data for the secondary image by itself, you see that\r\n      we have the full 8-bit information.\r\n   <\/p><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">figure;\r\nplot(pixelRow8(1, :), <span style=\"color: #A020F0\">'g'<\/span>); ylim([0 300]);\r\nlegend(<span style=\"color: #A020F0\">'Secondary Image'<\/span>, <span style=\"color: #A020F0\">'Location'<\/span>, <span style=\"color: #A020F0\">'NorthWest'<\/span>);\r\ntitle(<span style=\"color: #A020F0\">'Secondary Image'<\/span>);\r\nxlabel(<span style=\"color: #A020F0\">'Pixel Count'<\/span>); ylabel(<span style=\"color: #A020F0\">'Pixel Value (UINT8)'<\/span>);<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/imdecompdemo_06.png\"> <p>The following animations show how the secondary image data becomes more apparent as we subtract out the primary image data.\r\n      <i>Animations created using <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/18210-anymate\">ANYMATE<\/a>.<\/i><\/p>\r\n   <div>\r\n      <ul>\r\n         <li>2-D Animation<\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/animation2D.gif\"> <\/p>\r\n   <div>\r\n      <ul>\r\n         <li>3-D Animation<\/li>\r\n      <\/ul>\r\n   <\/div>\r\n   <p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/animation3D.gif\"> <\/p>\r\n   <h3>Embed Function &amp; Decode Function<a name=\"20\"><\/a><\/h3>\r\n   <p>These are the actual functions for creating and decoding embedded images.<\/p>\r\n   <div>\r\n      <ul>\r\n         <li><a href=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/embedImage.m\"><tt>embedImage.m<\/tt><\/a><\/li>\r\n      <\/ul>\r\n   <\/div><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">help <span style=\"color: #A020F0\">embedImage<\/span><\/pre><pre style=\"font-style:oblique\">  EMBEDIMAGE  Embed an image into another image\r\n    EMBEDIMAGE(PRIMARYIMAGE, IMAGETOEMBED) embeds the image file\r\n    IMAGETOEMBED into the image file PRIMARYIMAGE. Both PRIMARYIMAGE and\r\n    IMAGETOEMBED must be valid file names.\r\n \r\n    The image files must be 8-bit images. The output image file will be a\r\n    16-bit PNG image, named PRIMARYIMAGE_IMAGETOEMBED.png. The primary\r\n    image data will be stored in the most significant byte and the\r\n    embedded image data will be stored in the least significant byte.\r\n \r\n    Example:\r\n      embedImage('trees.tif', 'football.jpg');\r\n \r\n    See also DECODEIMAGE.\r\n \r\n  Jiro Doke\r\n  Jan 24, 2009.\r\n\r\n<\/pre><div>\r\n      <ul>\r\n         <li><a href=\"https:\/\/blogs.mathworks.com\/images\/loren\/173\/decodeImage.m\"><tt>decodeImage.m<\/tt><\/a><\/li>\r\n      <\/ul>\r\n   <\/div><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">help <span style=\"color: #A020F0\">decodeImage<\/span><\/pre><pre style=\"font-style:oblique\">  DECODEIMAGE  Decode embedded image.\r\n    DECODEIMAGE(IMAGEFILE) decodes embedded image file IMAGEFILE.\r\n    IMAGEFILE must be a valid file name. It will display a figure with 3\r\n    axes. The top axis is the original image. The bottom left is the\r\n    primary image, and the bottom right is the embedded hidden image.\r\n \r\n    [PRIMARY, HIDDEN] = DECODEIMAGE(IMAGEFILE) returns the primary image\r\n    and the hidden image data as RGB data.\r\n \r\n    This function only works on images created by embedImage.m.\r\n \r\n    Example:\r\n      % create embedded image\r\n      embedImage('trees.tif', 'football.jpg');\r\n      decodeImage('trees_football.png');\r\n \r\n    See also EMBEDIMAGE.\r\n \r\n  Jiro Doke\r\n  Jan 24, 2009.\r\n\r\n<\/pre><h3>Cleanup<a name=\"22\"><\/a><\/h3><pre style=\"background: #F9F7F3; padding: 10px; border: 1px solid rgb(200,200,200)\">iptsetpref(<span style=\"color: #A020F0\">'ImshowBorder'<\/span>, curImshowBorder);<\/pre><h3>Comments?<a name=\"23\"><\/a><\/h3>\r\n   <p>I hope you liked this example. Images make nice examples because they are visual. Let me know if you have other fun, pedagogical\r\n      examples that involve graphics. Put them on the <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/\">File Exchange<\/a>, and I'll be sure to take a look at it as a potential Pick of the Week!\r\n   <\/p><script language=\"JavaScript\">\r\n<!--\r\n\r\n    function grabCode_e2ac53da62f047b9b1b70b410771150d() {\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='e2ac53da62f047b9b1b70b410771150d ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' e2ac53da62f047b9b1b70b410771150d';\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 = 'Loren Shure';\r\n        copyright = 'Copyright 2009 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_e2ac53da62f047b9b1b70b410771150d()\"><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.7<br><\/p>\r\n<\/div>\r\n<!--\r\ne2ac53da62f047b9b1b70b410771150d ##### SOURCE BEGIN #####\r\n%% Decomposing Embedded Images\r\n% Today I\u00e2\u20ac\u2122d like to introduce a guest blogger,\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/authors\/15007 Jiro>,\r\n% who is an application engineer here at The MathWorks. Some of you may\r\n% know him as one of the bloggers for the <https:\/\/blogs.mathworks.com\/pick\/\r\n% File Exchange Pick of the Week>.\r\n\r\n%%\r\n% I recently presented to a group of freshman engineering students at\r\n% Virginia Commonwealth University. I wanted to show something that was\r\n% fun, eye-catching, and relatively easy to explain. So I created this\r\n% image-related demo. I am not an expert in image processing, but I\r\n% certainly had a lot of fun working on this example. For anyone interested\r\n% in hardcore image processing, I would suggest also taking a look at\r\n% <https:\/\/blogs.mathworks.com\/steve\/ Steve's Image Processing blog>.\r\n%\r\n% This example demonstrates how to embed an image into another image and\r\n% how to decompose the images. The embedded image is created by storing two\r\n% 8-bit RGB image as 16-bit RGB image. The primary image is stored in the\r\n% most significant byte, while the secondary image is stored in the least\r\n% significant byte. (See the section titled *\"Embed Function & Decode\r\n% Function\"* of this post for the code). With this method, the secondary\r\n% image can be concealed inside the primary image, without any loss of\r\n% information.\r\n%\r\n% This post focuses on the decomposition part of the demo.\r\n%\r\n% _This demo uses functions from the\r\n% <https:\/\/www.mathworks.com\/products\/image\/ Image Processing Toolbox>._\r\n\r\n%% Setup\r\n\r\ncurImshowBorder = iptgetpref('ImshowBorder');\r\niptsetpref('ImshowBorder', 'tight');\r\n\r\n%% Show Images\r\n% Here are two images that look the same. But are they??\r\n%\r\n% * peppers_BlueHills.png\r\n%\r\n% <<peppers_BlueHills.png>>\r\n% \r\n% * peppers_trees.png\r\n%\r\n% <<peppers_trees.png>>\r\n%\r\n% Would you believe me if I say these were very different images? Perhaps\r\n% you'll believe MATLAB:\r\n\r\nisequal(imread('peppers_BlueHills.png'), imread('peppers_trees.png'))\r\n\r\n%%\r\n% In fact, they have completely different images embedded in them. You just\r\n% can't tell with the naked eye.\r\n\r\n%% Image Decomposition\r\n% Let's try to decompose the image and see what's hidden inside.\r\n%\r\n% These are 16-bit RGB images. See the *\"Embed Function & Decode Function\"*\r\n% section of this report to see the function that I used for creating these\r\n% images.\r\n\r\nimData = imread('peppers_BlueHills.png');\r\nwhos imData\r\n\r\n%%\r\n% The image contains two 8-bit images. The primary image is stored in the\r\n% most significant byte and the secondary image is stored in the least\r\n% significant byte.\r\n%\r\n% *Convert RGB 3-D Array to a Vector*\r\n%\r\n% We'll be using |TYPECAST| to convert the data type, and the function\r\n% requires a vector.\r\n\r\npixelVals = imData(:);\r\npixelVals(1:10)\r\n\r\n%%\r\n% *Convert UINT16 to UINT8*\r\n%\r\n% Next, convert the data type from |UINT16| to |UINT8|. In doing so, we'll\r\n% use |TYPECAST| (instead of |CAST|) to preserve the data.\r\n\r\npixelValsConv = typecast(pixelVals, 'uint8');\r\nwhos pixelVals*\r\n\r\n%%\r\n% Notice that |pixelValsConv| has twice as many elements. This is because\r\n% there are two 8-bit values to a 16-bit value.\r\n%\r\n% *Separate Two Images*\r\n%\r\n% We'll reshape them to separate out the least and the most significant\r\n% bytes.\r\n\r\npixelValsConv = reshape(pixelValsConv, 2, [])';\r\npixelValsConv(1:10, :)\r\n\r\n%%\r\n% On a system with \"little-endian\" architecture, the first column is the\r\n% least significant byte and the second column is the most significant\r\n% column.\r\n%\r\n%   (first pixel)  62*256 + 225 = 16097\r\n%\r\n% On a \"big-endian\" architecture system, the order is switched. \r\n\r\n[cmp,maxsize,endian] = computer\r\n\r\nif strcmp(endian, 'L')\r\n  imOrder = [2 1];\r\nelse\r\n  imOrder = [1 2];\r\nend\r\n\r\n%%\r\n% We'll take each column and reshape them as the primary and secondary\r\n% images.\r\n\r\nimDataPrimary   = reshape(pixelValsConv(:, imOrder(1)), size(imData));\r\nimDataSecondary = reshape(pixelValsConv(:, imOrder(2)), size(imData));\r\n  \r\n%%\r\n% We can see that we end up with two images, both of which are now |UINT8|\r\n% images.\r\n\r\nwhos imData*\r\n\r\n%%\r\n% *Show Primary and Secondary Images*\r\n\r\nfigure;imshow(imDataPrimary);\r\nfigure;imshow(imDataSecondary);\r\n\r\n%% Decomposing the Second Image\r\n% We'll do the same thing for the second image we read in. Let's see what's\r\n% embedded in that one. We'll use <decodeImage.m |decodeImage.m|> which is\r\n% a function with the above algorithm.\r\n\r\n[imData2Primary, imData2Secondary] = decodeImage('peppers_trees.png');\r\nfigure; imshow(imData2Primary);\r\nfigure; imshow(imData2Secondary);\r\n\r\n%% How It Works\r\n% So, how does this work? Why is the secondary image unrecognizable by the\r\n% naked eye? That's because the secondary image is stored in the least\r\n% significant byte.\r\n%\r\n% To understand this, let's take a look at a single row of pixels in one of\r\n% the RGB planes. We'll look at row 150 of the red plane.\r\n\r\npixelRow16 = imData(150, :, 1);  % row 150, red plane\r\n\r\n%%\r\n% We covert this vector to |UINT8| using |TYPECAST|:\r\n\r\npixelRow8 = typecast(pixelRow16, 'uint8');\r\npixelRow8 = reshape(pixelRow8, 2, []);\r\npixelRow8(:, 1:10)\r\n\r\n%%\r\n% On a little-endian architecture system, the first row is the secondary\r\n% image, and the second row is the primary image. Next, we'll create a\r\n% |UINT16| vector with only the primary image.\r\n\r\n% set the secondary image vector to ZERO\r\npixelRow8Main  = [zeros(1, size(pixelRow8, 2), 'uint8'); pixelRow8(2, :)];\r\npixelRow16Main = typecast(pixelRow8Main(:)', 'uint16');\r\n\r\n%%\r\n% Now, let's compare the values of the total image vector with those of the\r\n% primary image vector.\r\n\r\nfigure;\r\nax1 = axes;hold on;\r\nplot(pixelRow16);\r\nplot(pixelRow16Main, 'r');\r\nxlabel('Pixel Count'); ylabel('Pixel Value (UINT16)');\r\nlegend('Total Image', 'Primary Image', 'Location', 'NorthWest');\r\nrectX = 160; rectY = 15000; rectW = 30; rectH = 3500;\r\nrectangle('Position', [rectX, rectY, rectW, rectH]);\r\ndar = get(ax1, 'DataAspectRatio'); dar = dar(2)\/dar(1);\r\nw = .3; h = w*(rectH\/rectW)\/dar;\r\nax2 = axes(...\r\n  'Units', 'Normalized', ...\r\n  'Position', [.6 .2 w h], ...\r\n  'Box', 'on', ...\r\n  'LineWidth', 2, ...\r\n  'XTick', [], ...\r\n  'Ytick', [], ...\r\n  'Color', [.95 .95 .95]);hold on;\r\nxlabel('Magnified Region');\r\nplot(pixelRow16);\r\nplot(pixelRow16Main, 'r');\r\nxlim([rectX, rectX+rectW]);\r\nylim([rectY, rectY+rectH]);\r\n\r\n%%\r\n% As this figure shows, the primary image represents the majority of the\r\n% information. The information for the secondary image is much smaller\r\n% relative to the primary image. But if we look at the data for the\r\n% secondary image by itself, you see that we have the full 8-bit\r\n% information.\r\n\r\nfigure;\r\nplot(pixelRow8(1, :), 'g'); ylim([0 300]);\r\nlegend('Secondary Image', 'Location', 'NorthWest');\r\ntitle('Secondary Image');\r\nxlabel('Pixel Count'); ylabel('Pixel Value (UINT8)');\r\n\r\n%%\r\n% The following animations show how the secondary image data becomes more\r\n% apparent as we subtract out the primary image data. _Animations created\r\n% using <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/18210-anymate\r\n% ANYMATE>._\r\n%\r\n% * 2-D Animation\r\n%\r\n% <<animation2D.gif>>\r\n%\r\n% * 3-D Animation\r\n%\r\n% <<animation3D.gif>>\r\n\r\n%% Embed Function & Decode Function\r\n% These are the actual functions for creating and decoding embedded images.\r\n%\r\n% * <embedImage.m |embedImage.m|>\r\n\r\nhelp embedImage\r\n\r\n%%\r\n% * <decodeImage.m |decodeImage.m|>\r\n\r\nhelp decodeImage\r\n\r\n%% Cleanup\r\n\r\niptsetpref('ImshowBorder', curImshowBorder);\r\n\r\n%% Comments?\r\n% I hope you liked this example. Images make nice examples because they are\r\n% visual. Let me know if you have other fun, pedagogical examples that\r\n% involve graphics. Put them on the\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/ File Exchange>, and\r\n% I'll be sure to take a look at it as a potential Pick of the Week!\r\n##### SOURCE END ##### e2ac53da62f047b9b1b70b410771150d\r\n-->","protected":false},"excerpt":{"rendered":"<p>\r\n   \r\n      Today I&#8217;d like to introduce a guest blogger, Jiro, who is an application engineer here at The MathWorks. Some of you may know him as one of the bloggers for the File Exchange Pick... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/loren\/2009\/02\/24\/decomposing-embedded-images\/\">read more >><\/a><\/p>","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[27],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/173"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/comments?post=173"}],"version-history":[{"count":0,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/173\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/media?parent=173"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/categories?post=173"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/tags?post=173"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}