{"id":1766,"date":"2015-01-30T00:00:11","date_gmt":"2015-01-30T08:00:11","guid":{"rendered":"http:\/\/192.168.3.4\/?p=1766"},"modified":"2018-01-09T06:49:49","modified_gmt":"2018-01-09T14:49:49","slug":"dynamic-images","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=1766","title":{"rendered":"Dynamic Images"},"content":{"rendered":"<p><strong>Introduction &#8211; There is more to the picture for the eye<\/strong><\/p>\n<p>In the last post we extracted data from images and processed it. \u00a0In this post we will cover the topic of image manipulation based on external data. \u00a0This is useful because it allows us to combine data sets into a single point of observation. \u00a0I&#8217;ll go through how to take an image and convert it based on variable conditions. \u00a0I&#8217;ll also cover how to overlay text data into an image. \u00a0Then I will cover the steps to upload the image to a web host.<\/p>\n<p>We have a lot to cover and it will be a springboard to a greater topic of augmented reality. \u00a0Lets begin.<\/p>\n<p><strong>Purpose &#8211;\u00a0Lets take a look at the\u00a0use of this method<\/strong><\/p>\n<p>A few months back, while researching topics, I came across a site that featured a <a href=\"http:\/\/www.nooganeer.com\/his\/projects\/homeautomation\/raspberry-pi-thermostat-part-1-overview\/\" target=\"_blank\">RPi thermostat<\/a>. \u00a0The one item that caught my attention was the graphical interface on the thermostat. \u00a0The developer had a picture of the home being controlled and conditional readings were overlaid on that image. \u00a0The end result is an intuitive experience for those without any technical knowledge. \u00a0It shows how the RPi can be used to bridge technology and provide a meaningful purpose. \u00a0Although folks starting out\u00a0in RPi development may not be ready to digest it, I feel that it\u00a0is a prime example of what can be done. \u00a0This post will go along the lines of the work that Jeff has done. \u00a0It\u00a0will give you the basic steps to achieve a graphical result that is similar.<\/p>\n<p><strong>Details &#8211; Getting it together so it looks like it should<\/strong><\/p>\n<p>The one thing that I would like to do in this post is to upload images to this blog using the RPi that represent real time conditions. \u00a0Here is an image of the 520 bridge with current traffic conditions. \u00a0It also contains the current date and time. \u00a0I&#8217;m also including the current temperature reading scrapped from NOAA.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"http:\/\/www.orangelinesolutions.com\/uploads\/images\/520_Bridge.png\" alt=\"\" width=\"628\" height=\"353\" \/><\/p>\n<p>First thing is first, I want to express how difficult it was to get a changed image to appear on this blog. \u00a0Originally I had intended to upload directly to this website. \u00a0For some strange reason\u00a0the old images would remain, even after uploading a new image. \u00a0I suspect a cache proxy is to blame, but I am not privileged enough to know that for sure. \u00a0So after much used time, I decided to upload the images to another host and point my blog to that as the source image. \u00a0I just want to point that out before continuing on.<\/p>\n<p>Now lets start with a base image.\u00a0 In my example above, I used an image found on google using an image search term &#8220;new 520 bridge&#8221;.\u00a0 It was used in a <a href=\"http:\/\/www.seattlepi.com\/local\/transportation\/article\/Construction-to-start-on-the-new-520-floating-3441908.php\" target=\"_blank\">Seattle PI article <\/a>about it.\u00a0 With the base image selected, we can now brand it with some text using IM.<\/p>\n<p>RPi has a narrow range of fonts to work with.\u00a0 I had to find a way to list them and <a href=\"http:\/\/stackoverflow.com\/questions\/1392858\/with-imagemagick-how-can-you-see-all-available-fonts\" target=\"_blank\">this site<\/a> had a great suggestion.\u00a0 I used this command to find out what I have.<\/p>\n<pre>\r\n<code>\r\nconvert -list font &gt; fontlist.txt\r\n<\/code>\r\n<\/pre>\n<p>I settled on this font for our example, &#8220;Nimbus-Mono-Bold-Oblique&#8221;.\u00a0 This command will create the text image we will use to overlay on to the base image.<\/p>\n<pre>\r\n<code>\r\nconvert -size 360x80 xc:none -fill white -pointsize 35 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,6 'West bound SR-520'\" statictext.png\r\n<\/code>\r\n<\/pre>\n<p>Now with that we can create our first text branding of the image.\u00a0 I used this command to do it.<\/p>\n<pre>\r\n<code>\r\ncomposite -watermark 20% -geometry -0-70 -gravity center statictext.png 520_Bridge.jpg 520_Bridge_Text.png\r\n<\/code>\r\n<\/pre>\n<p>Now is a good time to go through some of the code for each of the steps.\u00a0 In the first convert command, we created a new image called &#8220;statictext.png&#8221;.\u00a0 It is set with a Nimbus mono bold oblique font with a 35 pointsize.\u00a0 We gave it a white fill color and a transparent background.\u00a0 Then we set the image dimensions and positioned the text.<\/p>\n<p>Next, we used that generated image and did a composite command.\u00a0 Since we want to see the original image, I chose a watermark of 20%.\u00a0 Then I positioned the watermark off set from the center, up 70 pixels.<\/p>\n<p>Here is where the dynamic comes into play.\u00a0 We&#8217;ll be adding a date and time stamp to our image so we can see that it is indeed current.\u00a0 Here the first command to create the date layer image.<\/p>\n<pre>\r\n<code>\r\nconvert -size 93x20 xc:none -fill white -pointsize 15 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,2 '$(date +%Y' '%m' '%d)'\" date.png\r\n<\/code>\r\n<\/pre>\n<p>Now we are ready to create our time layer image using this command.<\/p>\n<pre>\r\n<code>\r\nconvert -size 125x22 xc:none -fill white -pointsize 20 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,2 'Time $(date +%H':'%M)'\" time.png\r\n<\/code>\r\n<\/pre>\n<p>With both of the time stamp images ready, we can now layer them on to the branded image.<\/p>\n<pre>\r\n<code>\r\ncomposite -watermark 20% -gravity NorthWest -geometry +5+5 time.png 520_Bridge_Text.png 520_Bridge_Text_Time.png\r\ncomposite -watermark 20% -gravity NorthWest -geometry +10+30 date.png 520_Bridge_Text_Time.png 520_Bridge_Text_TimeStamp.png\r\n<\/code>\r\n<\/pre>\n<p>Now comes the real magic.\u00a0 We&#8217;ll be scraping data from NOAA.\u00a0 I found <a href=\"http:\/\/www.mikeslab.net\/?p=172\" target=\"_blank\">an excellent post<\/a> that showed me the steps, which saved me an enormous amount of time.\u00a0 Thank you Mike!\u00a0 The process is to the point.\u00a0 The bonus is the XML format that NOAA uses.\u00a0 This allow easy polling of readings.\u00a0 Here is the python script I&#8217;ll be using.<\/p>\n<pre>\r\n<code>\r\n#!\/usr\/bin\/env python\r\nfrom lxml import etree\r\nimport urllib2\r\nimport os\r\n\r\nurl = 'http:\/\/w1.weather.gov\/xml\/current_obs\/KSEA.xml'\r\nfp = urllib2.urlopen(url)\r\ndoc = etree.parse(fp)\r\nfp.close()\r\n\r\ntemp = doc.xpath(\"\/\/current_observation\/temp_f\")[0].text\r\ntemp = temp[:-2]\r\ntemp_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_temp.txt\", \"w\")\r\ntemp_file.write(temp)\r\ntemp_file.close()\r\n\r\nrhum = doc.xpath(\"\/\/current_observation\/relative_humidity\")[0].text\r\nrhum_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_rhum.txt\", \"w\")\r\nrhum_file.write(rhum)\r\nrhum_file.close()\r\n\r\nwstrg = doc.xpath(\"\/\/current_observation\/wind_string\")[0].text\r\nwstrg_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_wstrg.txt\", \"w\")\r\nwstrg_file.write(wstrg)\r\nwstrg_file.close()\r\n\r\nvismi = doc.xpath(\"\/\/current_observation\/visibility_mi\")[0].text\r\nvismi_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_vismi.txt\", \"w\")\r\nvismi_file.write(vismi)\r\nvismi_file.close()\r\n<\/code>\r\n<\/pre>\n<p>The real beauty of this is we can get several readings to output as text files.\u00a0 This is useful because we&#8217;ll use them as variables in our bash script later on.\u00a0 Now for the last part, the traffic status.<\/p>\n<p>In this last section I want to layer text indicating the traffic condition.\u00a0 We will have 4 possible states.\u00a0 This is based on the data we scraped from the\u00a0WSDOT traffic image we used in the last post.\u00a0 These will be clear, moderate, heavy, congested, or no data.\u00a0 In order to bring these values over to our image, we&#8217;ll need to create conditions.\u00a0 Luckly, the readings have already been made.\u00a0 So all that needs to be done is read the text file containing the image color values.\u00a0 Based on the value, we can create our layer image accordingly.\u00a0 Here is the python script to do just that.<\/p>\n<pre>\r\n<code>\r\nHEXReading_file = open(\"\/home\/pi\/cacti_scripts\/HEXReading.txt\", \"r\")\r\nhead = HEXReading_file.read()\r\nHEXReading_file.close()\r\n\r\nif head in ('000000'):\r\n    os.system(\"convert -size 360x80 xc:none -fill black -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Congested'' condition.png\")\r\nelif head in ('FF0000'):\r\n    os.system(\"convert -size 360x80 xc:none -fill red -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Heavy'' condition.png\")\r\nelif head in ('FFFF00'):\r\n    os.system(\"convert -size 360x80 xc:none -fill orange -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Moderate'' condition.png\")\r\nelif head in ('20E040'):\r\n    os.system(\"convert -size 360x80 xc:none -fill green -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Clear'' condition.png\")\r\nelse:\r\n    os.system(\"convert -size 360x80 xc:none -fill grey -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'No Data'' condition.png\")\r\n<\/code>\r\n<\/pre>\n<p>Now all that is left is putting it all together with a shell script.\u00a0 This is the best part because CRON can run this single script.<\/p>\n<pre>\r\n<code>\r\n#!\/bin\/bash\r\n\r\n# Scrape the temperature data from NOAA's website\r\npython NOAA_KSEA.py\r\n\r\n# Create the image layer that contains the temperature data from NOAA\r\nnoaaTemp=$(&lt;\/home\/pi\/cacti_scripts\/NOAA_KSEA_temp.txt)\r\nconvert -size 135x25 xc:none -fill black -pointsize 20 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 1,1 'Temp $noaaTemp F'\" noaaTemp.png\r\n\r\n# Create the static text image layer\r\nconvert -size 360\u00d780 xc:none -fill black -pointsize 35 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,6 'West bound SR-520'\" statictext.png\r\n\r\n# Create the timestamp image layers\r\nconvert -size 93\u00d720 xc:none -fill black -pointsize 15 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,2 '$(date +%Y' '%m' '%d)'\" date.png\r\nconvert -size 125\u00d722 xc:none -fill black -pointsize 20 -font Nimbus-Mono-Bold-Oblique -gravity center -draw \"text 0,2 'Time $(date +%H':'%M)'\" time.png\r\n\r\n# Overlay the image layers on the baseline image\r\ncomposite -watermark 20% -geometry -0-70 -gravity center statictext.png 520_Bridge.jpg 520_Bridge_Text.png\r\ncomposite -watermark 20% -gravity NorthWest -geometry +5+5 time.png 520_Bridge_Text.png 520_Bridge_Time.png\r\ncomposite -watermark 20% -gravity NorthWest -geometry +10+30 date.png 520_Bridge_Time.png 520_Bridge_TimeStamp.png\r\ncomposite -watermark 20% -gravity NorthEast -geometry +5+5 noaaTemp.png 520_Bridge_TimeStamp.png 520_Bridge_Temp.png\r\ncomposite -gravity center condition.png 520_Bridge_Temp.png 520_Bridge.png\r\n\r\n# Upload the final image to the FTP host\r\nwput -u -nc -B \/home\/pi\/noaa\/520_Bridge.png 'ftp:\/\/&lt;ftpuser&gt;:&lt;ftppass&gt;@&lt;host:port&gt;\/&lt;path&gt;\/520_Bridge.png'\r\n<\/code>\r\n<\/pre>\n<p>You can clearly see that last command in the bash script uploads the file to a FTP host, that was easy.\u00a0 The script makes a reference to a python script, here is the code for that.<\/p>\n<pre>\r\n<code>\r\n#!\/usr\/bin\/env python\r\nfrom lxml import etree\r\nimport urllib2\r\nimport os\r\n\r\nurl = 'http:\/\/w1.weather.gov\/xml\/current_obs\/KSEA.xml'\r\nfp = urllib2.urlopen(url)\r\ndoc = etree.parse(fp)\r\nfp.close()\r\n\r\ntemp = doc.xpath(\"\/\/current_observation\/temp_f\")[0].text\r\ntemp = temp[:-2]\r\ntemp_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_temp.txt\", \"w\")\r\ntemp_file.write(temp)\r\ntemp_file.close()\r\n\r\nrhum = doc.xpath(\"\/\/current_observation\/relative_humidity\")[0].text\r\nrhum_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_rhum.txt\", \"w\")\r\nrhum_file.write(rhum)\r\nrhum_file.close()\r\n\r\nwstrg = doc.xpath(\"\/\/current_observation\/wind_string\")[0].text\r\nwstrg_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_wstrg.txt\", \"w\")\r\nwstrg_file.write(wstrg)\r\nwstrg_file.close()\r\n\r\nvismi = doc.xpath(\"\/\/current_observation\/visibility_mi\")[0].text\r\nvismi_file = open(\"\/home\/pi\/cacti_scripts\/NOAA_KSEA_vismi.txt\", \"w\")\r\nvismi_file.write(vismi)\r\nvismi_file.close()\r\n\r\nHEXReading_file = open(\"\/home\/pi\/cacti_scripts\/HEXReading.txt\", \"r\")\r\nhead = HEXReading_file.read()\r\nHEXReading_file.close()\r\n\r\nif head in ('000000'):\r\n    os.system(\"convert -size 360x80 xc:none -fill black -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Congested'' condition.png\")\r\nelif head in ('FF0000'):\r\n    os.system(\"convert -size 360x80 xc:none -fill red -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Heavy'' condition.png\")\r\nelif head in ('FFFF00'):\r\n    os.system(\"convert -size 360x80 xc:none -fill orange -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Moderate'' condition.png\")\r\nelif head in ('20E040'):\r\n    os.system(\"convert -size 360x80 xc:none -fill green -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'Clear'' condition.png\")\r\nelse:\r\n    os.system(\"convert -size 360x80 xc:none -fill grey -pointsize 55 -font Nimbus-Mono-Bold-Oblique -gravity center -draw 'text 0,6 'No Data'' condition.png\")\r\n<\/code>\r\n<\/pre>\n<p>With the shell script calling the python script, we can do a lot of stuff behind the scenes.\u00a0 The only thing left at this point is creating the CRON job, which you can reference how to do from my earlier post.\u00a0 In Webmin, I entered this to do the trick.<\/p>\n<pre>\r\n<code>\r\nbash \/&lt;path&gt;\/script.sh\r\n<\/code>\r\n<\/pre>\n<p>That&#8217;s it!\u00a0 Now I have the RPi processing new images at 5 minute intervals and posting them online.\u00a0 This concludes this topic.<\/p>\n<p><strong>Relations &#8211; getting more from the RPI with scripts<\/strong><\/p>\n<p>I had a tremendous learning curve to face with this topic.\u00a0 I was fortunate to find postings online that cleared up the subject for me.\u00a0 If you are like me, then the topic of Python is a vast open expanse.\u00a0 Here is a post with several instructional videos to help you make your way through the maze.<br \/>\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/sGcEAInD9Sc\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p><strong>Summary &#8211; the complete picture<br \/>\n<\/strong><\/p>\n<p>This topic covered the concepts of image manipulation using third party data.\u00a0 We used ImageMagick and Python on the RPi to accomplish this.\u00a0 Then we stepped through how the final image was uploaded to the internet.\u00a0 The entire process is run from a script that is scheduled to occur every 5 minutes.<\/p>\n<p>The material covered in this section will fundamental to future projects.\u00a0 Having the know how to bring it all together will instrumental in up coming posts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction &#8211; There is more to the picture for the eye In the last post we extracted data from images and processed it. \u00a0In this post we will cover the topic of image manipulation based on external data. \u00a0This is useful because it allows us to combine data sets into a single point of observation. \u00a0I&#8217;ll go through how to take an image and convert it based on variable conditions. \u00a0I&#8217;ll also cover how to overlay text data into an&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=1766\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,10,6,3],"tags":[],"class_list":["post-1766","post","type-post","status-publish","format-standard","hentry","category-computer-vision","category-data-mining","category-raspberry-pi","category-rd"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/1766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1766"}],"version-history":[{"count":47,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/1766\/revisions"}],"predecessor-version":[{"id":1903,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/1766\/revisions\/1903"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}