{"id":292,"date":"2018-12-04T13:29:42","date_gmt":"2018-12-04T17:29:42","guid":{"rendered":"http:\/\/dominionsw.com\/wordpress\/?p=292"},"modified":"2019-11-25T07:47:17","modified_gmt":"2019-11-25T11:47:17","slug":"classifying-tree-seedlings-with-machine-learning-part-1","status":"publish","type":"post","link":"https:\/\/www.dominionsw.com\/?p=292","title":{"rendered":"Classifying tree seedlings with Machine Learning, Part 1."},"content":{"rendered":"<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"341\" class=\"wp-image-302\" src=\"http:\/\/dominionsw.com\/wordpress\/wp-content\/uploads\/2018\/12\/GSBG-1-1024x341.png\" alt=\"\" srcset=\"https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/GSBG-1-1024x341.png 1024w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/GSBG-1-300x100.png 300w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/GSBG-1-768x256.png 768w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/GSBG-1-624x208.png 624w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/GSBG-1.png 1932w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\r\n<!-- \/wp:image -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>I&#8217;ve been working with a non-profit, <a href=\"https:\/\/greenstand.org\/\">GreenStand<\/a>, helping them with image processing tasks and related machine learning features for the technology part of their mission.<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>GreenStand is (really) making the world a better place &#8211; walkin&#8217; the walk.\u00a0 In short, they&#8217;re investing in people and local communities by encouraging them, financially and otherwise, to plant trees, because many of these communities have been deforested, for fuel and other reasons. You can read more about their mission <a href=\"https:\/\/greenstand.org\/about-greenstand\/about-our-mission\/\">here<\/a>.\u00a0<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>When I approached them about their needs, one of the first things that came up was how to identify &#8220;bad&#8221; photos. First, one has to define what a &#8220;bad&#8221; photo is. It turns out that the planters upload a photo of the newly planted seedling to demonstrate to the group that it has been planted. Since the cell phones used are often old and with low resolution cameras, and submitted over a typically spotty cell phone connection, the photos can be out of focus or distorted. Sometimes people upload the wrong photo, so the photo might be of a car, or some random scene. In addition, the photos are not taken in such a way as to make identification easier &#8211; by having the same pose, focal length, or removing other stray vegetation around the seedling.\u00a0<\/p>\r\n<h2>Duplicate photos<\/h2>\r\n<p>Another problem to be solved &#8211; and this is where the machine learning \/ deep learning comes in &#8211;\u00a0 is caused by people uploading in batches and the same photos are uploaded over and over again. Knowing the basic type of tree (if not the exact species) and its geolocation in the meta data of the photo, we can flag it as a potential duplicate, and keep from overloading the server with data.<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>Image processing for any task is completely dependent on the data &#8211; what approaches you take, the algorithms you choose, the results that you can attain &#8211; depend entirely upon the data you are given. Fortunately, the folks at GreenStand have been up and running long enough to give me over 50000 photos to start with. I&#8217;m going to be doing prototyping for this project with Matlab. Ultimately, to run on the server, likely to be a cloud based solution, I will have to port the code over to an open source solution &#8211; probably Python and OpenCV. But for rapid prototyping of image processing and machine learning, you really cannot beat Matlab, in my opinion. It&#8217;s pricey but it saves a lot of time.<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>The latest version of Matlab that I&#8217;m using, 2018b has an integrated image browser &#8220;app&#8221;, that let&#8217;s the user browse a folder of images and load them into various other apps or just import them into the user&#8217;s matlab workspace. I loaded up the images and browsed through them, getting an idea of what the &#8220;good&#8221; or &#8220;bad&#8221; image characteristics might be.<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:image {\"id\":306} -->\r\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"928\" height=\"1024\" class=\"wp-image-306\" src=\"http:\/\/dominionsw.com\/wordpress\/wp-content\/uploads\/2018\/12\/Capture-1-928x1024.png\" alt=\"\" srcset=\"https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-928x1024.png 928w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-272x300.png 272w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-768x848.png 768w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-624x689.png 624w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1.png 1366w\" sizes=\"auto, (max-width: 928px) 100vw, 928px\" \/><a href=\"https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-306\" src=\"https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1.png\" alt=\"\" width=\"1366\" height=\"1508\" srcset=\"https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1.png 1366w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-272x300.png 272w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-768x848.png 768w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-928x1024.png 928w, https:\/\/www.dominionsw.com\/wp-content\/uploads\/2018\/12\/Capture-1-624x689.png 624w\" sizes=\"auto, (max-width: 1366px) 100vw, 1366px\" \/><\/a>\r\n<figcaption>Image Browser with out of focus images<\/figcaption>\r\n<\/figure>\r\n<!-- \/wp:image -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>The image browser let&#8217;s you load images directly into other &#8220;apps&#8221;, such as the color segmentation app. This will come in very handy later. But for now I&#8217;m just looking at the out of focus images.<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>&nbsp;<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:heading -->\r\n<h2>&#8220;Focus&#8221; algorithms<\/h2>\r\n<!-- \/wp:heading -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>Determining if an image is out of focus is really determining the sharpness of the image &#8211; how &#8220;crisp&#8221; the image is. This means measuring contrast. There are quite a few algorithms that have been developed for this purpose. After reading up on the literature, and testing a few, I decided upon using the &#8220;Brenner Focus Algorithm&#8221;.\u00a0<br \/><br \/><a href=\"https:\/\/cs.uwaterloo.ca\/~vanbeek\/Publications\/spie2014.pdf\">https:\/\/cs.uwaterloo.ca\/~vanbeek\/Publications\/spie2014.pdf<\/a><\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>I implemented a variation on this algorithm by taking the average of the maximum of the square of the vertical and horizontal gradients:<\/p>\r\n<p>&nbsp;<\/p>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> Rr = \\{1...rows\\} <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> Rc = \\{1...cols\\} <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> \\forall r \\in Rr ,\\forall c \\in Rc <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> \\bigtriangledown H=I(r,c) - I(r,c-2) <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> \\bigtriangledown V=I(r,c) - I(r-2,c) <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> M = max(\\bigtriangledown H,\\bigtriangledown V) <\/span>\r\n<span class=\"wp-katex-eq\" data-display=\"false\"> F = mean(M) <\/span>\r\n<p>Matlab Code<\/p>\r\n<p>function [Measure] = brenners(Image)<br \/>\u00a0 \u00a0 [M, N] = size(Image);<br \/>\u00a0 \u00a0 DH = zeros(M, N,&#8217;single&#8217;);<br \/>\u00a0 \u00a0 DV = zeros(M, N,&#8217;single&#8217;);<br \/>\u00a0 \u00a0 DHG = gpuArray(DH);<br \/>\u00a0 \u00a0 DVG = gpuArray(DV);<br \/>\u00a0 \u00a0 IG = gpuArray(Image);<br \/>\u00a0 \u00a0 DVG(1:M-2,:) = IG(3:end,:)-IG(1:end-2,:);<br \/>\u00a0 \u00a0 DHG(:,1:N-2) = IG(:,3:end)-IG(:,1:end-2);<br \/>\u00a0 \u00a0 Mx = max(abs(DHG), abs(DVG)); <br \/>\u00a0 \u00a0 M2 = Mx.^2;<br \/>\u00a0 \u00a0 M2CPU = gather(M2);<br \/><br \/>\u00a0 \u00a0 Measure = mean2(M2CPU);<br \/>end<\/p>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>Java Code<\/p>\r\n<pre>\/\/ Compute average of sum of squares of the gradient in H and V directions.\r\n\tprivate static double brennersFocusMetric(int[][] input,int rows, int cols ) {\r\n\r\n\t\tint[][] V = new int[rows][cols];\r\n\t\tint[][] H = new int[rows][cols];\r\n\t\tfor(int row = 0; row &lt; rows; row++)\r\n\t\t{\r\n\t\t\tfor (int col = 0; col &lt; cols-2; col++) {\r\n\t\t\t\tint grad = input[row][col+2] - input[row][col];\r\n\t\t\t\tH[row][col] = grad;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor(int row = 0; row &lt; rows-2; row++)\r\n\t\t{\r\n\t\t\tfor (int col = 0; col &lt; cols; col++) {\r\n\t\t\t\tint grad = input[row+2][col] - input[row][col];\r\n\t\t\t\tV[row][col] = grad;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tdouble sum = 0;\r\n\t\tfor(int row = 0; row &lt; rows; row++)\r\n\t\t{\r\n\t\t\tfor (int col = 0; col &lt; cols; col++) {\r\n\t\t\t\tdouble HRC = H[row][col];\r\n\t\t\t\tdouble VRC = V[row][col];\r\n\t\t\t\tsum  += Math.abs(HRC) &gt; Math.abs(VRC) ? HRC * HRC : VRC * VRC;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn sum \/ (double)(rows * cols);\r\n\t}<\/pre>\r\n<!-- \/wp:paragraph -->\r\n\r\n<!-- wp:paragraph -->\r\n<p>The above code finds the &#8220;bad&#8221; photos well &#8211; based on my test, it eliminated all the out of focus photos, with a less than 5 percent false positive rate. Some of these false positives were the result of a dual focus &#8211; the seedling was in focus, but the background was not. The algorithm returns a number that describes a measure, somewhat subjective, of the &#8220;goodness&#8221; of focus. One will need to experiment to find the range values best suited for their own application. Of course, more parameters of what is &#8220;bad&#8221; can be used to refine the meaning of the term. The goal is to warn the user that their image of the seedling is not good, and they should retake the photo.<\/p>\r\n<h2>Next&#8230;.<\/h2>\r\n<p>In the next of this series of 4 posts, I&#8217;ll go into segmentation of the images and preparation of them for testing a Support Vector Machine that classifies the images into four basic types&#8230;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<!-- \/wp:paragraph -->","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working with a non-profit, GreenStand, helping them with image processing tasks and related machine learning features for the technology part of their mission. GreenStand is (really) making the world a better place &#8211; walkin&#8217; the walk.\u00a0 In short, they&#8217;re investing in people and local communities by encouraging them, financially and otherwise, to plant [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-292","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/posts\/292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=292"}],"version-history":[{"count":102,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/posts\/292\/revisions"}],"predecessor-version":[{"id":607,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=\/wp\/v2\/posts\/292\/revisions\/607"}],"wp:attachment":[{"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dominionsw.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}