<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1477876132593701158</id><updated>2012-01-27T11:51:06.998+01:00</updated><category term='PiCloud'/><category term='cloud computing'/><category term='radiometric normalization'/><category term='kernel PCA'/><category term='Python Numpy GAE Picloud REST'/><category term='OpenCV'/><category term='GAE'/><category term='IDL'/><category term='GDAL'/><category term='IR-MAD'/><category term='image fusion'/><category term='change detection'/><category term='image analysis'/><category term='panchromatic sharpening'/><category term='Mathematica'/><category term='ENVI/IDL'/><category term='Game Theory'/><category term='GPILib'/><category term='python'/><category term='CUDA'/><category term='NUMPY'/><category term='MAD'/><category term='MKL'/><category term='OpenLayers'/><category term='PyCublas'/><category term='CUBLAS'/><category term='Appengine'/><title type='text'>Fun with, inter alia, ENVI-IDL</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2351223517173163452</id><published>2012-01-22T14:03:00.001+01:00</published><updated>2012-01-27T11:51:07.009+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='image analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='PiCloud'/><category scheme='http://www.blogger.com/atom/ns#' term='Appengine'/><category scheme='http://www.blogger.com/atom/ns#' term='panchromatic sharpening'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><category scheme='http://www.blogger.com/atom/ns#' term='image fusion'/><title type='text'>Pansharpening on the Google Appengine</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: left;"&gt;One of the more elegant panchromatic sharpening techniques in multispectral image processing makes use of the &lt;a href="http://en.wikipedia.org/wiki/Discrete_wavelet_transform"&gt;discrete wavelet transform&lt;/a&gt;&amp;nbsp;(DWT). I describe it in some detail in my&lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt; book&lt;/a&gt; and provide an ENVI/IDL script for it on my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;software site&lt;/a&gt;. I also mentioned running it on the cloud with small images in an&lt;a href="http://fwenvi-idl.blogspot.com/2010/12/pan-sharpening-on-gae.html"&gt; earlier blog&lt;/a&gt;. Using &lt;a href="https://www.picloud.com/"&gt;PiCloud&lt;/a&gt; as a backend for the App Engine, it wasn't difficult to get the algorithm to run on much larger images on my &lt;a href="http://ms-image-analysis.appspot.com/static/index.html"&gt;GAE App&lt;/a&gt;. The picture below shows a small subset of an Ikonos image with the upsampled four-meter resolution multispectral image at the center. Bands 1,2 and 3 are shown as an RGB-composite. The DWT pansharpened scene, calculated with the App using the one-meter resolution panchromatic image included with the Ikonos data, is shown on the left. For comparison, the result of applying ENVI's built-in Gram-Schmidt algorithm is shown to the right. Notice that the colors match better for DWT.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-1q7hP08ekNM/TxwDH62Kl5I/AAAAAAAAAwI/215xfH7XFEk/s1600/pansharp.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="132" src="http://1.bp.blogspot.com/-1q7hP08ekNM/TxwDH62Kl5I/AAAAAAAAAwI/215xfH7XFEk/s400/pansharp.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;These are just 400x400 pixel cutouts but the full pansharpened scene is 2000x2000 pixels, about the largest size the App can handle just now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2351223517173163452?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2351223517173163452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2351223517173163452' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2351223517173163452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2351223517173163452'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2012/01/pansharpening-on-google-appengine.html' title='Pansharpening on the Google Appengine'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-1q7hP08ekNM/TxwDH62Kl5I/AAAAAAAAAwI/215xfH7XFEk/s72-c/pansharp.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2810005056815709523</id><published>2011-12-22T13:38:00.000+01:00</published><updated>2011-12-22T13:45:18.083+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenLayers'/><category scheme='http://www.blogger.com/atom/ns#' term='image analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='PiCloud'/><category scheme='http://www.blogger.com/atom/ns#' term='Appengine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><category scheme='http://www.blogger.com/atom/ns#' term='NUMPY'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenCV'/><title type='text'>Multispectral Image Analysis on the Cloud (again)</title><content type='html'>Well, I've made a few fairly big jumps forward, as I continue to " muck about"&amp;nbsp;on the &lt;a href="http://code.google.com/intl/en/appengine/"&gt;Google Appengine&lt;/a&gt;. The biggest jump was to get &lt;a href="http://numpy.scipy.org/"&gt;Numpy&lt;/a&gt; to work, at long last. Another was to make extensive use of &lt;a href="https://www.picloud.com/"&gt;PiCloud&lt;/a&gt; as a background service, another to use&lt;a href="http://code.google.com/p/svgfig/"&gt; SvgFig&lt;/a&gt; to render 2-D plots on the client side, and yet another to "mashup" with &lt;a href="http://openlayers.org/"&gt;OpenLayers&lt;/a&gt; to show image footprints on the world map. Picloud, as I mentioned before, provides Python wrappers for &lt;a href="http://www.gdal.org/"&gt;GDAL&lt;/a&gt;, but also has the Python wrapper for the computer vision package &lt;a href="http://opencv.willowgarage.com/documentation/python/index.html"&gt;OpenCV&lt;/a&gt; pre-installed. This opens up all sorts of nice tools. So far, I've only implemented the &lt;a href="http://en.wikipedia.org/wiki/Canny_edge_detector"&gt;Canny edge detector&lt;/a&gt;, but there will be more to come.&lt;br /&gt;&lt;br /&gt;I have not enabled billing in the GAE, so I just get 28 "instance hours" a day, each with 600 Mhz and 128 MB. Also my free storage quota is 5 GB (for me and all users together). I'm getting a much better environment on PiCloud (1.2 GHz, 20 hours per month free CPU time), but I can only send 5 MB payloads to PiCloud from the Appengine. Based on past experience, these limits will continue to get better as time goes by and so will, I hope, the &lt;a href="http://ms-image-analysis.appspot.com/display.html"&gt;App&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Getting all this stuff to work (more or less) would have been impossible for me without the very friendly help from the Appengine team, the people at PiCloud and the altruistic contributors to &lt;a href="http://stackoverflow.com/"&gt;StackOverflow&lt;/a&gt;. If any of you guys are reading this blog, then&lt;br /&gt;&lt;br /&gt;Many thanks, Season's Greetings, and I'll be bugging you again in the New Year!&lt;br /&gt;&lt;br /&gt;Mort&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2810005056815709523?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2810005056815709523/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2810005056815709523' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2810005056815709523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2810005056815709523'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/12/multispectral-image-analysis-on-cloud.html' title='Multispectral Image Analysis on the Cloud (again)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3282034243209639741</id><published>2011-12-09T14:04:00.001+01:00</published><updated>2011-12-09T16:09:01.907+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IDL'/><category scheme='http://www.blogger.com/atom/ns#' term='image analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='radiometric normalization'/><category scheme='http://www.blogger.com/atom/ns#' term='ENVI/IDL'/><title type='text'>Scatterplot Matching</title><content type='html'>&lt;a href="http://www.mdpi.com/2072-4292/2/7/1644/"&gt;S. J. Maas and N. Rajan&amp;nbsp;(2010) Remote Sensing 2, 1644-1661&lt;/a&gt;, describe a method to radiometrically normalize the RED and NIR bands of multitemporal Landsat TM images which uses the characteristic shape of NIR vs RED scatterplots. The so-called&lt;i&gt;&amp;nbsp;bare soil line &lt;/i&gt;(BSL&lt;i&gt;)&amp;nbsp;&lt;/i&gt;and f&lt;i&gt;ull canopy point &lt;/i&gt;(FCP)&amp;nbsp;derived from the scatterplots of target and reference image&amp;nbsp;are used to re-scale the RED and NIR bands of the target to match those of the reference. The procedure works with other platforms too, and is illustrated below for two ASTER images (the RED band is VNIR band 2 and the NIR band is VNIR band 3):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-MqSUVDkREPE/TuIY9B5APuI/AAAAAAAAAvw/c-AF8XbNtkA/s1600/bslfcpnorm.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="180" src="http://1.bp.blogspot.com/-MqSUVDkREPE/TuIY9B5APuI/AAAAAAAAAvw/c-AF8XbNtkA/s640/bslfcpnorm.jpg" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;The red lines are the derived BSLs and the red crosses mark the FCPs in the reference (left) and target (center) scatterplots. The scatterplot of the normalized target image (right) closely matches that of the reference image (left). The images below are RGB images of (R=band 2, G=band 3, B = band2) for reference (left), target (center) and normalized target (right) on the scale (0-255):&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-AdT6N6wF-FM/TuIZRQPrTKI/AAAAAAAAAv4/1Mk8RPuCQhI/s1600/bslfcpnormim.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="211" src="http://4.bp.blogspot.com/-AdT6N6wF-FM/TuIZRQPrTKI/AAAAAAAAAv4/1Mk8RPuCQhI/s640/bslfcpnormim.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Notice that the normalized target colors are very similar to those of the reference. We can use the&lt;a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=5362"&gt; IR-MAD normalization method&lt;/a&gt; to check the result. The plots &amp;nbsp;below show the reference vs. target regression lines using pseudo-invariant pixels identified by IR-MAD &amp;nbsp;before (top row) &amp;nbsp;and after normalization (bottom row).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-MJGqMz81dzg/TuIZeJlLl2I/AAAAAAAAAwA/A_RWghVtajc/s1600/bslfcpnormregr.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="512" src="http://1.bp.blogspot.com/-MJGqMz81dzg/TuIZeJlLl2I/AAAAAAAAAwA/A_RWghVtajc/s640/bslfcpnormregr.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;The slope and intercepts are as follows:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;top left (band 2)&lt;/b&gt;: &amp;nbsp; &amp;nbsp; &amp;nbsp;slope 1.49 &amp;nbsp;intercept 11.7 &amp;nbsp; &amp;nbsp;&lt;b&gt; top right (band 3)&lt;/b&gt; slope 1.40 intercept -3.44&lt;br /&gt;&lt;b&gt;bottom left (band 2)&lt;/b&gt; slope 0.92 &amp;nbsp;intercept 17.6 &amp;nbsp; &amp;nbsp;&lt;b&gt; top right (band 3) &lt;/b&gt;slope 1.02 intercept &amp;nbsp;1.07&lt;br /&gt;&lt;br /&gt;So the correction seems to work somewhat better for the NIR band 3. Here is an &lt;a href="http://mcanty.homepage.t-online.de/idl/bslfcpnorm.zip"&gt;ENVI/IDL extension for scatterplot normalization&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3282034243209639741?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3282034243209639741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3282034243209639741' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3282034243209639741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3282034243209639741'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/12/scatterplot-matching.html' title='Scatterplot Matching'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-MqSUVDkREPE/TuIY9B5APuI/AAAAAAAAAvw/c-AF8XbNtkA/s72-c/bslfcpnorm.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2367693968990152406</id><published>2011-11-15T12:41:00.001+01:00</published><updated>2011-11-15T13:18:49.364+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='image analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='GDAL'/><category scheme='http://www.blogger.com/atom/ns#' term='PiCloud'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><category scheme='http://www.blogger.com/atom/ns#' term='NUMPY'/><title type='text'>Still waiting for Numpy, and a bug in GDAL</title><content type='html'>The latest Python software development kit for Google's &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;App Engine&lt;/a&gt; (GAE) offers experimental support for Python 2.7, concurrent programming and a number of C-libraries, one of which is Numpy (Numerical Python). I've been waiting for Numpy for a long time in order to make my&lt;a href="http://ms-image-analysis.appspot.com/static/index.html"&gt; image analysis Webb App&lt;/a&gt; efficient, or more accurately, usable. Unfortunately there are problems and I still &lt;a href="http://groups.google.com/group/google-appengine-python/browse_thread/thread/d1234236a37c1f6b#"&gt;can't import Numpy&lt;/a&gt; into my GAE scripts. I'm hoping things will be sorted out in the next release.&lt;br /&gt;&lt;br /&gt;Another source of trial and tribulation is&lt;a href="http://www.gdal.org/"&gt; GDAL&lt;/a&gt; (Geospatial Data Abstraction Library). I'm using it in my App &amp;nbsp;to read and write raster data from different standard formats. To do this, I need to shove the imagery back and forth between GAE and &lt;a href="https://www.picloud.com/"&gt;PiCloud&lt;/a&gt; since GAE has no GDAL support. That's complicated enough, but it turns out that GDAL doesn't handle georeferenced images which are rotated away form north-up correctly. The rotation is ignored. A lot of satellite imagery is supplied path-oriented (oriented along the satellite trajectory) so that isn't very nice. So far no advice from the GDAL developers' forum or from &lt;a href="http://stackoverflow.com/questions/8009912/python-gdal-package-does-not-handle-rotated-raster-images-correctly"&gt;StackOverflow&lt;/a&gt; either.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2367693968990152406?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2367693968990152406/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2367693968990152406' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2367693968990152406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2367693968990152406'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/11/still-waiting-for-numpy-and-bug-in-gdal.html' title='Still waiting for Numpy, and a bug in GDAL'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3661570456168958303</id><published>2011-10-26T12:27:00.000+02:00</published><updated>2011-10-26T12:29:13.726+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PiCloud'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><title type='text'>Free quotas on PiCloud</title><content type='html'>&lt;a href="https://www.picloud.com/"&gt;PiCloud&lt;/a&gt; announced yesterday that it will provide users &lt;a href="http://blog.picloud.com/2011/10/24/20-free-core-hours-every-month/"&gt;with 20 free c1 core hours&lt;/a&gt;&amp;nbsp; per month. At their very reasonable prices that's only $1.00 worth of CPU time, but it means that you don't have to enable billing at all to be able to experiment with and/or use the service indefinitely!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3661570456168958303?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3661570456168958303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3661570456168958303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3661570456168958303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3661570456168958303'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/10/free-quotas-on-picloud.html' title='Free quotas on PiCloud'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1993236881876947048</id><published>2011-10-15T12:02:00.000+02:00</published><updated>2011-10-15T12:08:52.980+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CUDA'/><category scheme='http://www.blogger.com/atom/ns#' term='CUBLAS'/><category scheme='http://www.blogger.com/atom/ns#' term='kernel PCA'/><category scheme='http://www.blogger.com/atom/ns#' term='PyCublas'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='NUMPY'/><title type='text'>A Python script for Kernel PCA on the GPU</title><content type='html'>Just to wrap up the discussion in my last post but one: Although the pyCublas wrapper for matrix multiplication on the GPU doesn't bring much of a speedup relative to the CPU when I use Numpy together with the Math Kernel Library on 64bit Windows, it may well do so in other configurations. I only have the one machine with a CUDA-enabled graphics card, so I can't experiment. You can now download the code for both versions (with and without CUDA) from my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;software page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By the way, pyCublas won't work for CUDA 4.x because nvidia has changed the &lt;a href="http://developer.download.nvidia.com/compute/DevZone/docs/html/CUDALibraries/doc/CUBLAS_Library.pdf"&gt;CUBLAS API &lt;/a&gt;completely.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1993236881876947048?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1993236881876947048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1993236881876947048' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1993236881876947048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1993236881876947048'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/10/python-script-for-kernel-pca-on-gpu.html' title='A Python script for Kernel PCA on the GPU'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-8718170125374115688</id><published>2011-10-11T16:39:00.000+02:00</published><updated>2011-10-11T17:09:30.456+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GDAL'/><category scheme='http://www.blogger.com/atom/ns#' term='PiCloud'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>PiCloud adds GDAL</title><content type='html'>For anyone using (or thinking of using) the Cloud Sevice &lt;a href="https://www.picloud.com/"&gt;PiCloud&lt;/a&gt; for image analysis, here is great news:&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;Hi Mort,&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;We decided to add GDAL to our systems by default. If you run a job now that uses the gdal Python library (import gdal), it should work. Let us know how it goes!&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;Ken Elkabany&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;PiCloud, Inc.&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-8718170125374115688?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/8718170125374115688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=8718170125374115688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8718170125374115688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8718170125374115688'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/10/picloud-adds-gdal.html' title='PiCloud adds GDAL'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6203288497229835744</id><published>2011-10-08T21:22:00.001+02:00</published><updated>2011-10-11T17:10:45.671+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IDL'/><category scheme='http://www.blogger.com/atom/ns#' term='CUDA'/><category scheme='http://www.blogger.com/atom/ns#' term='CUBLAS'/><category scheme='http://www.blogger.com/atom/ns#' term='GPILib'/><category scheme='http://www.blogger.com/atom/ns#' term='PyCublas'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='MKL'/><category scheme='http://www.blogger.com/atom/ns#' term='NUMPY'/><title type='text'>Numpy is FAST!</title><content type='html'>After upgrading my 32bit &lt;a href="http://code.google.com/p/pythonxy/wiki/Welcome"&gt;Python(XY)&lt;/a&gt; package to version 2.7, I discovered to my horror that Eclipse/Pydev was no longer there. It is actually the only part of the package that I regularly use. So I decided to "make nails with heads on them" as we say in German, and do my own  64bit Python 2.7 installation. In doing so, I discovered a great deal. To begin with, for anyone interested, here's how I went about it on my Windows machine:&lt;br /&gt;&lt;br /&gt;- installed the &lt;a href="http://www.chip.de/downloads/Java-Runtime-Environment-64-Bit_42224883.html"&gt;64bit Java Runtime&lt;/a&gt; (this is what Eclipse runs on)&lt;br /&gt;- intalled &lt;a href="http://www.eclipse.org/downloads/"&gt;Eclipse 3.7.1&lt;/a&gt; (win32-x86_64)&lt;br /&gt;- installed &lt;a href="http://pydev.org/"&gt;pydev&lt;/a&gt; onto Eclipse&lt;br /&gt;- installed&lt;a href="http://www.python.org/getit/releases/2.7/"&gt; 64bit Python 2.7&lt;/a&gt;&lt;br /&gt;&amp;nbsp;- installed &lt;a href="http://www.lfd.uci.edu/%7Egohlke/pythonlibs/"&gt;Numpy 1.6&lt;/a&gt; (win-amd64-Py2.7)&lt;br /&gt;- registered the Python interpreter with Pydev.&lt;br /&gt;&lt;br /&gt;That's it.&lt;br /&gt;&lt;br /&gt;Then it occured to me that I could now make use of the 64bit CUDA drivers/toolkit with my new Python installation. Not wanting to write my own CUDA kernels, there seemed to be three possibilities available to me:&lt;br /&gt;&lt;a href="http://mathema.tician.de/software/pycuda"&gt;PyCuda&lt;/a&gt;&lt;br /&gt;&lt;a href="http://scikits.cuda/"&gt;SciKits.Cuda&lt;/a&gt;&lt;br /&gt;&lt;a href="http://kered.org/blog/2009-04-13/easy-python-numpy-cuda-cublas/"&gt;PyCublas&lt;/a&gt;&lt;br /&gt;My experience with using CUDA in IDL has been that the only really substantial speedups involve matrix multiplication on the GPU. This means that the CUBLAS library should be interfaced to whatever script language one is using in order to take full advantage of NVIDIA optimized &lt;a href="http://en.wikipedia.org/wiki/BLAS"&gt;BLAS&lt;/a&gt; (Basic Linear Algebra Subprograms) code, as is the case, for example, with &lt;a href="http://www.txcorp.com/products/GPULib/"&gt;GPULib&lt;/a&gt; in IDL. This eliminated PyCuda, which explicitly excludes CUBLAS. SciKits.Cuda has Python wrappers for CUBLAS, but doesn't support the Windows platform (sheesh). That left PyCublas, written by Derek Anderson, a very simple and pretty wrapper for matrix multiplication using&lt;a href="http://docs.python.org/library/ctypes.html"&gt; ctypes&lt;/a&gt; to wrap the CUBLAS DLL. &lt;br /&gt;&lt;br /&gt;For later comparison with Python, here is what I get for IDL with and without GPULib when multiplying two 2048x2048 matrices together:&lt;br /&gt;&lt;br /&gt;&lt;listing&gt;Welcome to GPULib 1.4.4 (Revision: 2107)Graphics card: GeForce GTS 450, compute capability: 2.1, memory: 752 MB available, 993 MB totalChecking GPU memory allocation...no errors      0.10899997       2.5430000&lt;/listing&gt;The first number is the GPU time, the second the time required by IDL on the CPU, so the speedup is about 25x.&lt;br /&gt;Now for Python:&lt;br /&gt;&lt;br /&gt;&lt;listing&gt;[pycublas] time (CUBLAS): 0.171000s[pycublas] time (numpy): 68.578000s[pycublas] speedup: 401.0X&lt;/listing&gt;Wow! Python/Numpy is much slower than IDL and using CUDA really makes sense. However, I then deinstalled Numpy and replaced it with &lt;a href="http://www.lfd.uci.edu/%7Egohlke/pythonlibs/"&gt;Numpy compiled against the Intel Math Kernel Library&lt;/a&gt; (MKL). On my machine (which has an Intel Core i5 CPU) I get&lt;br /&gt;&lt;br /&gt;&lt;listing&gt;[pycublas] time (CUBLAS): 0.172000s[pycublas] time (numpy): 0.218000s[pycublas] speedup: 1.3X&lt;/listing&gt;So, in this configuration at least, CUDA is of little or no advantage.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6203288497229835744?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6203288497229835744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6203288497229835744' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6203288497229835744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6203288497229835744'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/10/numpy-is-fast.html' title='Numpy is FAST!'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-8644515919722894957</id><published>2011-09-06T10:41:00.000+02:00</published><updated>2011-09-06T10:41:21.060+02:00</updated><title type='text'>RADCAL Update</title><content type='html'>I just realized that I forgot to include spectral wavelengths, when available, in images normalized with &lt;a href="http://mcanty.homepage.t-online.de/idl/radcal.zip"&gt;RADCAL&lt;/a&gt;. That's important if you're normalizing in order to compare vegetation indices, for example. Fixed that now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-8644515919722894957?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/8644515919722894957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=8644515919722894957' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8644515919722894957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8644515919722894957'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/09/radcal-update.html' title='RADCAL Update'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6989713444290714293</id><published>2011-08-20T15:10:00.000+02:00</published><updated>2011-08-20T15:10:16.997+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python Numpy GAE Picloud REST'/><title type='text'>Numpy at last</title><content type='html'>"When sorrows come, they come not single spies, but in battalions." Fortunately this rule sometimes also applies to "joys". Almost simultaneously (at least within a few days of eachother) the following happened:&lt;br /&gt;&lt;br /&gt;1. &lt;a href="https://www.picloud.com/"&gt;Picloud&lt;/a&gt; announced its REST registration interface, which allows Picloud functions to be called from the &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;Google App Engine&lt;/a&gt; (GAE). So CPU-intensive calculations can be "farmed out" to Picloud servers. The servers support numpy, scipy and virually anything else you need.&lt;br /&gt;&lt;br /&gt;2. Google &lt;a href="http://groups.google.com/group/google-appengine-python/browse_thread/thread/4bde29d8e6dc0842#"&gt;announced&lt;/a&gt; that GAE will soon be running on Python 2.7 and, furthermore, will include numpy on its runtime.&lt;br /&gt;&lt;br /&gt;3. Dan Sanderson &lt;a href="http://groups.google.com/group/google-appengine-python/browse_thread/thread/659879520590d30f#"&gt;announced&lt;/a&gt; the initial drafts of the second edition of his book Programming Google App Engine. No doubt he will discuss the new features in detail.&lt;br /&gt;&lt;br /&gt;So it looks like, at long last, I will be able to port my ENVI/IDL algorithms to GAE in an efficient, IDL-Matlab-type syntax rather than continuing to torture myself with slow pure Python versions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6989713444290714293?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6989713444290714293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6989713444290714293' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6989713444290714293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6989713444290714293'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/08/numpy-at-last.html' title='Numpy at last'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5857712216548616448</id><published>2011-08-14T17:14:00.000+02:00</published><updated>2011-08-14T17:14:17.791+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Game Theory'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematica'/><title type='text'>Mathematica and Game Theory</title><content type='html'>Inter alia = "among other things", and it's about time that I made this change to my Blog title. &lt;br /&gt;&lt;br /&gt;Quite a few years ago I wrote a book on algorithmic game theory called &lt;a href="http://www.amazon.com/exec/obidos/tg/detail/-/0121588556/qid=1099033780/sr=1-1/ref=sr_1_1/104-0498814-5846344?v=glance&amp;s=books"&gt;Resolving Conflicts with Mathematica&lt;/a&gt;. It was built around the (at the time) most recent version of Wolfram Inc.'s &lt;a href="http://www.wolfram.com/mathematica/"&gt;Mathematica&lt;/a&gt;, namely Version 5.2. The &lt;a href="http://mcanty.homepage.t-online.de/bimatrix.zip"&gt;software package&lt;/a&gt; on my Home Page was, till now, only compatible with Version 5.2. I think I've fixed that. I say "think" because I stopped upgading at Version 6. I believe, though, that Versions 7 and 8 are backward compatible to Version 6 (but not to 5.2).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5857712216548616448?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5857712216548616448/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5857712216548616448' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5857712216548616448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5857712216548616448'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/08/mathematica-and-game-theory.html' title='Mathematica and Game Theory'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3548231689763995624</id><published>2011-08-04T15:51:00.000+02:00</published><updated>2011-08-04T15:51:26.117+02:00</updated><title type='text'>Image Analysis on the Cloud: New App Deployed</title><content type='html'>I just deployed the new, improved &lt;a href="http://ms-image-analysis.appspot.com/static/index.html"&gt;multispectral image analysis app&lt;/a&gt; to Google's cloud service (Google App Engine), see my previous blog. It still has a lot of quirks, one reason being that memory size restrictions apply on the cloud which are neither simulated on the development server nor, as far as I can see, anywhere documented. Anyway, it's fun.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3548231689763995624?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3548231689763995624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3548231689763995624' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3548231689763995624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3548231689763995624'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/08/image-analysis-on-cloud-new-app.html' title='Image Analysis on the Cloud: New App Deployed'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3805713156365788323</id><published>2011-07-24T22:06:00.004+02:00</published><updated>2011-07-24T22:32:29.212+02:00</updated><title type='text'>Image Analysis on the Cloud (Progress report)</title><content type='html'>I just spent a whole weekend modifying my Google App Engine App. I haven't released the new version yet, prior to more testing. I now use the so-called Blobstore to store the users' image data, so that larger images can in principle be processed. The image formats correspond more closely to those generated by my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;ENVI/IDL routines&lt;/a&gt;. For example, the MAD variates are now stored in floating point format with a chi-square band appended. I'd previously been byte-stretching them to save space.&lt;br /&gt;&lt;br /&gt;I've limited the upload size to 8MB uncompressed multispectral images in byte format (formerly 1MB), allowing for up to 32MB processed images in floating point format (for instance the result of a principal components, Fourier or IR-MAD transformation). There are two main reasons for the limitation:&lt;br /&gt;&lt;br /&gt;1. My free Blobstore quota is 1GB, and a single user can fill up to 5 image slots. So in the worst case, if every user fills her slots to the maximum 5 x 32MB = 160MB, I can only serve 1000 mod 160 = 6 users. (Unless I start paying Google for more resources, which I don't intend to do.)&lt;br /&gt;&lt;br /&gt;2. There is still no Numpy, so the algorithms are very slow and would be &lt;i&gt;hopelessly&lt;/i&gt; slow for larger images. IR-MAD runs on the App Engine task queue and calls itself recursively for each iteration. Since the task response time deadline is 10 minutes, this allows for up to 10 minutes per iteration. With Numpy this would be plenty, but in pure Python the itertations are very slow. For two 1000 x 1000 x 6 byte LANDSAT images, each iteration takes around 4 minutes. I have the App send the user an email notification when the calculation finishes.&lt;br /&gt;&lt;br /&gt;I have enquired with &lt;a href="https://www.picloud.com/"&gt;Picloud&lt;/a&gt; about the possibility of "farming out" computationally intensive functions from the Google App Engine. Since Picloud has Numpy/Scipy, this would solve my problem. The Picloud team is looking into it and I may be asked to beta-test. Of course, they eventually want to see cash, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3805713156365788323?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3805713156365788323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3805713156365788323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3805713156365788323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3805713156365788323'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/07/image-analysis-on-cloud-progress-report.html' title='Image Analysis on the Cloud (Progress report)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-9026703493404348889</id><published>2011-07-02T21:27:00.009+02:00</published><updated>2011-07-04T20:46:22.456+02:00</updated><title type='text'>IDLe moments</title><content type='html'>With nothing better to do this evening I decided to add some gadgets to the blog design. The "SEARCH THIS BLOG" box on the right is probably useful now that there are almost 60 pages of my blatherings. I also took the opportunity to update my photo with one that more closely corresponds to my not inconsiderable age. &lt;br /&gt;&lt;br /&gt;Still waiting for the Google  people to enable scientific computing in Python on their fantastic &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;App Engine platform&lt;/a&gt;, which someone went so far as to call an "operating system for the internet". My &lt;a href="http://groups.google.com/group/google-appengine-python/browse_thread/thread/60b1261980549ade#"&gt;begging&lt;/a&gt; on the App Engine/Python discussion group the other day drew a friendly response from Greg D'Alesandre, App Engine Senior Product Manager:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Hi Mort,&lt;br /&gt;&lt;br /&gt;Your time (and, um, begging) isn't wasted, we hear you and we know this is a very important ask and are prioritizing it with our other work.  Unfortunately at this time I don't have anything to announce about it...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So maybe one day ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-9026703493404348889?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/9026703493404348889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=9026703493404348889' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/9026703493404348889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/9026703493404348889'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/07/idle-moments.html' title='IDLe moments'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2263608110241659057</id><published>2011-06-24T22:04:00.005+02:00</published><updated>2011-06-30T16:40:03.671+02:00</updated><title type='text'>Modern IDL</title><content type='html'>I ordered Michael Galloy's new book &lt;a href="http://michaelgalloy.com/2011/06/14/modern-idl-available-now.html"&gt;Modern IDL&lt;/a&gt; last week. The hardcopy hasn't arrived yet (I'm in Germany) but Mike sent me a link to the PDF version and I've browsed through it. In my own work I use IDL mainly for algorithm implementation and leave most of the graphics user interface, data I/O, map transforms and other pizazz to ENVI. But I've always wanted a thorough, concise, up-to-date overview of the the IDL language and its vast capabilities. This is &lt;span style="font-style:italic;"&gt;exactly&lt;/span&gt; what Mike's book provides in 464 very informative pages. Can't wait to have the "real thing" on my bookshelf. Highly recommended!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2263608110241659057?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2263608110241659057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2263608110241659057' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2263608110241659057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2263608110241659057'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/06/modern-idl.html' title='Modern IDL'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6041949491926472311</id><published>2011-06-23T10:20:00.006+02:00</published><updated>2011-06-23T12:14:55.301+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='image analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='radiometric normalization'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><title type='text'>Scatterplots for the Google Cloud App</title><content type='html'>If you run automatic radiometric normalization on my GAE &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;App&lt;/a&gt; you can now plot the orthogonal regression lines next to the table of regression coefficients (click to enlarge):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-G4OU7WP6GUo/TgMQgsMyLrI/AAAAAAAAAus/Y9Fa_kxsj_k/s1600/normalization.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 189px;" src="http://2.bp.blogspot.com/-G4OU7WP6GUo/TgMQgsMyLrI/AAAAAAAAAus/Y9Fa_kxsj_k/s400/normalization.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5621354913695149746" /&gt;&lt;/a&gt;&lt;br /&gt;The App uses the HTML5 CANVAS object to draw the plots, so it should run on any up-to-date browser, with the exception (of course) of IE9.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6041949491926472311?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6041949491926472311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6041949491926472311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6041949491926472311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6041949491926472311'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/06/scatterplots-for-google-cloud-app.html' title='Scatterplots for the Google Cloud App'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-G4OU7WP6GUo/TgMQgsMyLrI/AAAAAAAAAus/Y9Fa_kxsj_k/s72-c/normalization.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1380946823923409528</id><published>2011-05-17T21:32:00.004+02:00</published><updated>2011-05-17T21:43:59.803+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IR-MAD'/><category scheme='http://www.blogger.com/atom/ns#' term='GAE'/><category scheme='http://www.blogger.com/atom/ns#' term='change detection'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><category scheme='http://www.blogger.com/atom/ns#' term='MAD'/><title type='text'>IR-MAD on the Google App Engine</title><content type='html'>I just taught myself how to use background tasks in the App Engine (a lot easier than I thought). The IR-MAD algorithm now will run completely in background in my &lt;a href="http://6.mortcanty-1.appspot.com/static/index.html"&gt;App&lt;/a&gt;, so you no longer have to iterate manually. Things are happening at last with the GAE, see John K. Waters' &lt;a href="http://adtmag.com/articles/2011/05/11/appengine-backends.aspx"&gt;blog.&lt;/a&gt; I'm still hoping that Numpy will be made available, and if it is, then the sky's the limit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1380946823923409528?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1380946823923409528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1380946823923409528' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1380946823923409528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1380946823923409528'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/05/ir-mad-on-google-app-engine.html' title='IR-MAD on the Google App Engine'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7001635008570860662</id><published>2011-05-09T10:11:00.005+02:00</published><updated>2011-05-09T10:30:42.030+02:00</updated><title type='text'>iMAD on  Mac OS</title><content type='html'>Gillian Galford from &lt;a href="http://www.whrc.org/"&gt;Woods Hole Research Center&lt;/a&gt; figured out how to build the PROVMEANS DLM on a Mac.  This code will place the library PROVMEANS.SO in the default DLM path (and it couldn't be simpler):&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;pro make_provmeans&lt;br /&gt;&lt;br /&gt;PRINT, !MAKE_DLL.COMPILER_NAME&lt;br /&gt;&lt;br /&gt;INDIR = '/Applications/itt/canty/auxil'&lt;br /&gt;OUTDIR = $&lt;br /&gt; '/Applications/itt/idl/idl80/bin/bin.darwin.x86_64'&lt;br /&gt;&lt;br /&gt;MAKE_DLL, 'provmeans', 'IDL_Load', $&lt;br /&gt; INPUT_DIRECTORY=INDIR, $&lt;br /&gt; OUTPUT_DIRECTORY=OUTDIR, /SHOW_ALL_OUTPUT, $&lt;br /&gt; compile_directory=outdir&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;After running that, just copy the description file PROVMEANS.DLM from my auxil package to the same OUTDIR and you're all set. (The DLM is used by iMAD for calculating weighted means and variance-covariance matrices.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7001635008570860662?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7001635008570860662/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7001635008570860662' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7001635008570860662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7001635008570860662'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/05/imad-on-mac-os.html' title='iMAD on  Mac OS'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6306791711808309645</id><published>2011-04-12T20:44:00.002+02:00</published><updated>2011-04-12T21:00:09.082+02:00</updated><title type='text'>Python script for kernel PCA</title><content type='html'>The &lt;a href="http://mcanty.homepage.t-online.de/kpca_py.zip"&gt;script&lt;/a&gt; has pretty much the same functionality as the ENVI/IDL program described in my &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;book&lt;/a&gt;, except that it doesn't take advantage of CUDA. This is not because Python has no wrappers for CUDA, but because I'm programming on x64 Windows with a 64-bit driver/CUDA toolkit. Being a green novice at Python, I have no idea how to set up my own scientific Python computing environment, so I'm working with the (very nice) &lt;a href="http://www.pythonxy.com/"&gt;Pythonxy&lt;/a&gt; distribution. It has everything from Numpy to Scipy to GDAL to Eclipse/Pydev but is, alas, 32 bit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6306791711808309645?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6306791711808309645/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6306791711808309645' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6306791711808309645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6306791711808309645'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/04/python-script-for-kernel-pca.html' title='Python script for kernel PCA'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6325107855304228891</id><published>2011-03-29T11:34:00.007+02:00</published><updated>2011-03-29T13:15:22.319+02:00</updated><title type='text'>The MADMAN Rides Again</title><content type='html'>I've just finished modifiying the Python scripts for IR-Mad and radiometric normalization to allow for &lt;span style="font-weight:bold;"&gt;spatial and spectral subsetting&lt;/span&gt; as well as for &lt;span style="font-weight:bold;"&gt;radiometric normalization of large scenes&lt;/span&gt;. I have now included their documentation in the &lt;a href="https://docs.google.com/viewer?a=v&amp;pid=explorer&amp;chrome=true&amp;srcid=0B7IDSHlM4SgEMzg2YzFlMjAtNTBlZS00NGFmLWI4ZmUtMjlkYjhlZGIxYzli&amp;hl=en"&gt;MAD MAN&lt;/a&gt; which originally described the ENVI/IDL extensions only.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6325107855304228891?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6325107855304228891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6325107855304228891' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6325107855304228891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6325107855304228891'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/03/madman-rides-again.html' title='The MADMAN Rides Again'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4255279323050177867</id><published>2011-03-19T17:42:00.016+01:00</published><updated>2011-03-19T22:01:59.480+01:00</updated><title type='text'>Radiometric Normalization with Python</title><content type='html'>(Maybe I should start calling this blog "Fun Without ENVI-IDL".)  I've just improved the Python RADCAL script to allow a user to normalize large images. You can get both iMAD and RADCAL together with a readme &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;here&lt;/a&gt;. The syntax is now as follows:&lt;br /&gt;&lt;br /&gt;To run iMad on images &lt;span style="font-style: italic;"&gt;fname1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;fname2&lt;/span&gt;, which have the same spatial and spectral dimensions and are co-registered:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;python iMad.py fname1 fname2&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;This generates the output file&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;MAD[fname1-fname2]&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;To normalize &lt;span style="font-style: italic;"&gt;fname2&lt;/span&gt; to &lt;span style="font-style: italic;"&gt;fname1&lt;/span&gt;, (optionally) setting a minimum no-change probability threshold of 0.9 (default 0.95) and then (optionally) using the regression coefficients to normalize a larger file called &lt;span style="font-style: italic;"&gt;bigfname2&lt;/span&gt; having the same spectral dimension (e.g. a full scene) to &lt;span style="font-style: italic;"&gt;fname1&lt;/span&gt;&lt;listing&gt;&lt;br /&gt;python radcal.py MAD[fname1-fname2]&lt;br /&gt;                  -ncpt 0.9 -fsfn bigfname2&lt;br /&gt;&lt;/listing&gt;This creates the normalized output files&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;fname2_norm&lt;br /&gt;bigfname2_norm&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Several image formats are allowed and can even be mixed, see my earlier blogs. Here is an example with ENVI standard files &lt;span style="font-style: italic;"&gt;n1small&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;n2small&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;n2&lt;/span&gt;, all located in the directory &lt;span style="font-style: italic;"&gt;\imagery\gdal\&lt;/span&gt; (click to enlarge):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-ItYvztiB6mA/TYTdus_mzTI/AAAAAAAAAuI/nRLccDHNm1c/s1600/imad_radcal.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 250px;" src="http://3.bp.blogspot.com/-ItYvztiB6mA/TYTdus_mzTI/AAAAAAAAAuI/nRLccDHNm1c/s400/imad_radcal.jpg" alt="" id="BLOGGER_PHOTO_ID_5585833232267922738" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Before you can use the scripts, you must have the right Python environment installed. I'm running Windows 7 x64 with Python(xy) Version 2.6.5.6. You can get it &lt;a href="http://www.pythonxy.com/"&gt;here&lt;/a&gt;, and it's childsplay to install. Just be sure to include the GDAL plugin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4255279323050177867?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4255279323050177867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4255279323050177867' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4255279323050177867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4255279323050177867'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/03/radiometric-normalization-with-python.html' title='Radiometric Normalization with Python'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-ItYvztiB6mA/TYTdus_mzTI/AAAAAAAAAuI/nRLccDHNm1c/s72-c/imad_radcal.jpg' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-760420521695600721</id><published>2011-02-23T12:29:00.010+01:00</published><updated>2011-02-23T15:24:11.778+01:00</updated><title type='text'>Porting IR-MAD to Python (continued)</title><content type='html'>I described a Python port of IR-MAD in an &lt;a href="http://fwenvi-idl.blogspot.com/2011/01/porting-ir-mad-to-python.html"&gt;earlier blog&lt;/a&gt;. The IR-MAD algorithm is used by quite a few people to do automatic radiometric normalization, so I have added a Python script called radcal.py which can be used for normalization. In order to keep things simple (if a bit inflexible) I changed the file naming conventions slightly from the previous post:&lt;br /&gt;&lt;br /&gt;If you have two co-registered images having the same spatial/spectral dimensions, say &lt;span style="font-family:courier new;"&gt;image1.tif&lt;/span&gt; in GeoTiff format and &lt;span style="font-family:courier new;"&gt;image2.img &lt;/span&gt;in ERDAS IMAGINE format, then you can run IR-MAD on them as follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;python iMad.py path1/image1.tif path2/image2.img&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The MAD result will be written to the file&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;path1/MAD(image1.tif-image2.img).tif&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;in other words, in the same directory and with the same format as the first image.&lt;br /&gt;&lt;br /&gt;Now, to radiometrically normalize image2 to image1, you can type&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;python radcal.py path1/MAD(image1.tif-image2.img).tif [noChangeThresh]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;where the optional parameter &lt;span style="font-family:courier new;"&gt;noChangeThresh&lt;/span&gt; is the desired minimum probability of no change for selecting the invariant pixels to be used in the normalization (default 0.95). The script will write the normalized image to&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;path1/image2_norm.img&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;thus placing it in the same directory as image1 and maintaining its original format.&lt;br /&gt;&lt;br /&gt;If you want to normalize image1 to image2,  just run IR-MAD the other way round, i.e.,&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;python iMad.py path2/image2.img path1/image1.tif&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can download both iMad.py and radcal.py from &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;my software page&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-760420521695600721?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/760420521695600721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=760420521695600721' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/760420521695600721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/760420521695600721'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/02/porting-ir-mad-to-python-continued.html' title='Porting IR-MAD to Python (continued)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-745845275180703545</id><published>2011-02-07T11:44:00.026+01:00</published><updated>2011-02-10T17:09:20.424+01:00</updated><title type='text'>Face Recognition with IDL</title><content type='html'>Google's &lt;a href="http://picasa.google.com/"&gt;Picasa&lt;/a&gt; has a  very impressive face recognition function. Just naming a few of the hundreds or thousands of faces that the program extracts out of a photo collection is sufficient to categorize a large fraction of the remaining faces correctly.  The more unknown faces the user labels, the more exhaustive Picasa's classification becomes.&lt;br /&gt;&lt;br /&gt;I don't have any idea how Picasa face recognition works and I expect they are not going to tell me. But I do know that one standard technique makes use of linear and nonlinear principal components analysis (PCA), subjects which I deal with in some detail in my &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;book&lt;/a&gt;. The original suggestion goes back to &lt;a href="http://cbio.ensmp.fr/%7Ejvert/svn/bibli/local/Kirby1990Application.pdf"&gt;Kirby and Sirovich (1990)&lt;/a&gt; and is, basically, as follows:&lt;br /&gt;&lt;br /&gt;Given, say, M labeled faces (the training set) each consisting of N pixel intensity values, find the directions in N-dimensional "face space" which maximize the variance (spread) in faces. This is an eigenvalue problem with M solutions (for M less than N). The directions (eigenvectors) are called in this case "eigenfaces". Next, project each of the M labeled faces along the first k eigenfaces, where k is usually smaller than M. The k-component vector of projections comprises the principal components of each face.  Now, given an unlabeled face which is, however, known to be one of the persons in the training set, determine its principal component vector as well. Finally, give it the label of the training set face whose principal component vector is nearest. By using the so-called kernel trick, the whole procedure can be carried out in a nonlinear face space which (perhaps) can characterize the faces more efficiently. For the mathematically minded, here is a more &lt;a href="https://docs.google.com/viewer?a=v&amp;amp;pid=explorer&amp;amp;chrome=true&amp;amp;srcid=0B7IDSHlM4SgEODE5ZjljY2ItNjJhNC00NWE2LTg1ZTQtMzQ4YjJjNDNmODFl&amp;amp;hl=en"&gt;detailed explanation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I wrote a short &lt;a href="http://mcanty.homepage.t-online.de/facerec__define.zip"&gt;face recognition routine&lt;/a&gt; in IDL in the form of an object class which uses both linear and nonlinear (kernel) PCA as described above. There are several public domain &lt;a href="http://www.face-rec.org/databases/"&gt;face recognition databases&lt;/a&gt; on the net, but I decided it would be more fun to use my own, as generated by Picasa. I constructed training sets of (on average) about 130 examples drawn randomly from Picasa face images of 30 relatives and friends (so each person is represented by about 4 different faces). I resampled them all to 100x120-pixel b/w images. Here are the first 25 (ghostly and ghastly-looking) eigenfaces generated from one of the training sets:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-3VszZmUCYxg/TVQBEESAdEI/AAAAAAAAAt4/w5Y_I8Jexng/s1600/eigenfaces.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 333px; height: 400px;" src="http://3.bp.blogspot.com/-3VszZmUCYxg/TVQBEESAdEI/AAAAAAAAAt4/w5Y_I8Jexng/s400/eigenfaces.jpg" alt="" id="BLOGGER_PHOTO_ID_5572079808344519746" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Then I tested with (on average) about 170 new faces for the same 30 persons (so 5-6 faces per subject), counting the number of correct assignments. For linear PCA the hit rate was 13%, for kernel PCA with a Gaussian kernel it jumped to 36%. That may not sound very impressive, but the probability of getting it right by pure chance is one in thirty or about 3%. It's also interesting that nonlinear PCA does so much better a job.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-745845275180703545?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/745845275180703545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=745845275180703545' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/745845275180703545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/745845275180703545'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/02/face-recognition-with-idl.html' title='Face Recognition with IDL'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-3VszZmUCYxg/TVQBEESAdEI/AAAAAAAAAt4/w5Y_I8Jexng/s72-c/eigenfaces.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5238345145973604075</id><published>2011-01-28T10:03:00.009+01:00</published><updated>2011-01-29T17:09:47.942+01:00</updated><title type='text'>SVD on the GPU Revisited</title><content type='html'>A while ago I wrote an IDL DLM wrapper for &lt;a href="http://www.culatools.com/"&gt;CULA Tools&lt;/a&gt; Singular Value Decomposition  called CUDA_SVD, see my &lt;a href="http://fwenvi-idl.blogspot.com/2010/10/singular-value-decomposition-on-gpu.html"&gt;previous blog&lt;/a&gt;. I just tested it with an inexpensive, and therefore somewhat castrated, Fermi GPU (Asus GeForce GTS 450, 1Gb, €125.00) running on a Core i5 PC with 64b Windows 7. Instead of the earlier 7-fold acceleration for a 2000x2000 matrix relative to the IDL procedure LA_SVD, the speedup is now almost 11-fold (Abscissa: Number of matrix elements, Ordinate: Speedup):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/TUKHE5ekR0I/AAAAAAAAAto/ZDgDJboXN98/s1600/speedups64.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 286px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/TUKHE5ekR0I/AAAAAAAAAto/ZDgDJboXN98/s400/speedups64.jpg" alt="" id="BLOGGER_PHOTO_ID_5567160607601608514" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5238345145973604075?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5238345145973604075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5238345145973604075' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5238345145973604075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5238345145973604075'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/01/svd-on-gpu-revisited.html' title='SVD on the GPU Revisited'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_-NwiSqQlzPY/TUKHE5ekR0I/AAAAAAAAAto/ZDgDJboXN98/s72-c/speedups64.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7492459842328573415</id><published>2011-01-24T09:30:00.009+01:00</published><updated>2011-01-24T13:01:18.296+01:00</updated><title type='text'>Compiling an IDL DLM on Windows x64</title><content type='html'>After installing 64 bit ENVI/IDL on my new Windows 7 PC, I had to face the problem of recompiling my DLMs to the x64 platform. It turns out to be quite easy to do with Microsoft Visual C++ 2010 Express if you know which settings to change, which I didn't. After two frustrating days reading MS documentation and Visual Studio forums, I got the decisive tip from, of all places, Wikipedia. Basically you have to:&lt;br /&gt;&lt;br /&gt;- install the full Visual C++ Express (free from MS)&lt;br /&gt;&lt;br /&gt;- install the latest Microsoft Windows SDK (also free, includes the 32bit-64bit cross compiler called from Visual C++, which in turn runs as a 32bit application on 64bit Windows)  &lt;br /&gt;&lt;br /&gt;- set the IDL include and library paths of the Visual C++ project (the DLM) to the corresponding locations for the 64bit IDL version&lt;br /&gt;&lt;br /&gt;- from the Visual C++ Configuration Manager, choose the x64 target platform (this becomes available only after installing the SDK)&lt;br /&gt;&lt;br /&gt;- from the project's property sheet, change the Platform Toolset to the Windows SDK instead of the built-in v100 (this essential little point was in Wikipedia)   &lt;br /&gt;&lt;br /&gt;So the first thing I did was to make a 64-bit version of the provmeans DLM, which is required if you want to run my &lt;a href="http://docs.google.com/fileview?id=0B7IDSHlM4SgEMzYzOTQ1YTAtY2ZmYS00ZjI2LWIyZWQtNTU2ZWYyNzMwNDQy&amp;hl=en"&gt;IR-MAD ENVI extension&lt;/a&gt; on a 64bit Windows OS. There are now two versions in the &lt;a href="http://mcanty.homepage.t-online.de/idl/auxil.zip"&gt;AUXIL.ZIP&lt;/a&gt; package:&lt;br /&gt;&lt;br /&gt;provmeans.x86.dll  - for 32bit Windows&lt;br /&gt;provmeans.x86_64.dll - for 64bit Windows&lt;br /&gt;&lt;br /&gt;With this naming convention IDL will automatically load the appropriate version.&lt;br /&gt;&lt;br /&gt;My shiny new PC also has a GeForce GTS 450 graphics card so I can finally start playing around with the FERMI GPU. More on that later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7492459842328573415?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7492459842328573415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7492459842328573415' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7492459842328573415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7492459842328573415'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/01/compiling-idl-dlm-on-windows-x64.html' title='Compiling an IDL DLM on Windows x64'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6165003199473850113</id><published>2011-01-01T19:51:00.018+01:00</published><updated>2011-01-08T14:05:16.925+01:00</updated><title type='text'>Porting IR-MAD to Python</title><content type='html'>If I wait until Google App Engine allows Python scripts to use numpy/scipy I'll be old and gray. Come to think of it, I'm already old and gray. So I've done what I guess is the next best thing, namely a direct port of the ENVI/IDL &lt;a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=4695"&gt;IR-MAD&lt;/a&gt; (iMAD) script to Python/numpy/scipy. My main obstacle was how to read in different image formats, since if the user has ENVI/IDL or &lt;a href="http://www2.imm.dtu.dk/~aa/software.html"&gt;Matlab&lt;/a&gt; anyway, he or she won't need a Python version. I knew about &lt;a href="http://www.gdal.org/"&gt;GDAL&lt;/a&gt; and the &lt;a href="http://trac.osgeo.org/gdal/wiki/GdalOgrInPython"&gt;GDAL Python bindings&lt;/a&gt;, but the documentation is, shall we say, sparse? What got me off the ground was Chris Gerrard's very nice &lt;a href="http://www.gis.usu.edu/%7Echrisg/python/"&gt;Geoprocessing with Python&lt;/a&gt; programming class material.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://mcanty.homepage.t-online.de/imad_py.zip"&gt;script&lt;/a&gt; requires the Python packages numpy, scipy and osgeo/gdal and assumes that the GDAL binaries are correctly installed, see the links above. I'm using &lt;a href="http://code.google.com/p/pythonxy/"&gt;Pythonxy&lt;/a&gt;, which has everything you need apart from GDAL.&lt;br /&gt;&lt;br /&gt;Usage: iMad &lt;span style="font-style: italic;"&gt;infilename1 infilename2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;where &lt;span style="font-style: italic;"&gt;infilename1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;infilename2&lt;/span&gt; are fully qualified file names referring to geometrically registered, multispectral images having the same spatial and spectral dimensions. They can be in any of the following formats:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;ENVI Standard   e.g. image1&lt;br /&gt;                     image1.hdr&lt;br /&gt;&lt;br /&gt;PCI Geomatics   e.g. image1.pix&lt;br /&gt;&lt;br /&gt;ERDAS IMAGINE   e.g. image1.img&lt;br /&gt;&lt;br /&gt;ARCVIEW Raster  e.g. image1.bil&lt;br /&gt;                     image1.hdr&lt;br /&gt;&lt;br /&gt;TIFF/GeoTIFF    e.g. image1.tif&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;These formats can be mixed, for example the first image might be GeoTIFF and the second image ERDAS IMAGINE, but the output file will be written in the same format as that of the first image, with filename in this case&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;MAD(image1-image2).tif&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;The iMAD algorithm iterates to convergence or terminates after 50 iterations. The output file is stored in BSQ floating point format and consists of the stacked MAD variates in order of decreasing variance followed by the chi-square image, i.e., same as with my &lt;a href="http://mcanty.homepage.t-online.de/idl/mad.zip"&gt;ENVI/IDL script&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Next project will be a port of automatic radiometric normalization.&lt;br /&gt;&lt;br /&gt;Happy New Year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6165003199473850113?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6165003199473850113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6165003199473850113' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6165003199473850113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6165003199473850113'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2011/01/porting-ir-mad-to-python.html' title='Porting IR-MAD to Python'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2069035284222478539</id><published>2010-12-05T15:46:00.019+01:00</published><updated>2010-12-05T21:55:08.147+01:00</updated><title type='text'>Pan-sharpening on the GAE</title><content type='html'>Improving the spatial resolution of multispectral (MS) images via fusion with a higher resolution panchromatic (PAN) image is a common processing technique. The goal is to sharpen the MS image while at the same time preserving its spectral properties as much as possible. One of the best methods I know to achieve this involves the &lt;a href="http://en.wikipedia.org/wiki/Discrete_wavelet_transform"&gt;discrete wavelet transform&lt;/a&gt; (DWT). You can perform DWT pansharpening of small images on my &lt;a href="http://mortcanty-1.appspot.com/"&gt;Google App Engine app&lt;/a&gt;. The routine is a port to Python of the IDL program described in my &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;book&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The screenshot below shows, on the left, a 1 m resolution panchromatic Ikonos image and, on the right, the same image after two applications of a DWT filter.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/TPuzo2YHnCI/AAAAAAAAAqY/WqxTn9Ah3Xk/s1600/dwt.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 198px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/TPuzo2YHnCI/AAAAAAAAAqY/WqxTn9Ah3Xk/s400/dwt.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5547224880409713698" /&gt;&lt;/a&gt;&lt;br /&gt;The small square in the upper left hand corner on the right has been compressed by a factor of 4, so its spatial resolution is 4 m, the rest of the right hand image consists of the so-called &lt;span style="font-style:italic;"&gt;wavelet coefficients&lt;/span&gt;, i.e., the high spatial frequency details. These allow a lossless reconstruction of the panchromatic image. We can now fuse the pan image with one of the 4 m resolution MS bands by substituting the latter for the small square and inverting the transform (there is also a normalization step required), thereby mixing the high frequency PAN details with the MS band. This can be done for all of the MS bands successively. The image below shows the original MS image (bands 1,2,3 in RGB) magnified by a factor of four (left) and the pan-sharpened image (right).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_-NwiSqQlzPY/TPv7eWiP_6I/AAAAAAAAAqo/c3Pwv_b3DeI/s1600/mspansharp.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 200px;" src="http://2.bp.blogspot.com/_-NwiSqQlzPY/TPv7eWiP_6I/AAAAAAAAAqo/c3Pwv_b3DeI/s400/mspansharp.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5547303864900779938" /&gt;&lt;/a&gt;&lt;br /&gt;For more details see Section 5.3 in my &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;book&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2069035284222478539?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2069035284222478539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2069035284222478539' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2069035284222478539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2069035284222478539'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/12/pan-sharpening-on-gae.html' title='Pan-sharpening on the GAE'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_-NwiSqQlzPY/TPuzo2YHnCI/AAAAAAAAAqY/WqxTn9Ah3Xk/s72-c/dwt.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3227283900215751061</id><published>2010-11-24T20:30:00.005+01:00</published><updated>2010-11-24T21:23:45.025+01:00</updated><title type='text'>GPULib 1.4.2</title><content type='html'>I recently installed ENVI 4.8/IDL 8.0 and, along with them, the latest release of &lt;a href="http://gpulib.blogspot.com/2010/11/gpulib-142-released.html"&gt;GPULib&lt;/a&gt;. The Windows installation is now very slick and taylored to Windows nerds like me: You just run GPULib-setup.exe and say whether your CUDA graphics card supports single precision, double precision or (lucky you!) has a Fermi GPU. That's it. Then, provided you have your Nvidia driver and CUDA Toolkit correctly configured, you can open IDL, point your IDL and DLM paths to the GPULib /lib directory and start parallel programming. &lt;br /&gt;&lt;br /&gt;With the variable objects and operator overloading introduced in this version of GPULib, GPU programming is really getting nice. The code below transfers two large arrays a and b to the GPU, matrix multiplies the transpose of a with b, returns the result to the CPU and frees the graphics memory:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;   a_gpu = gpuPutArr(randomu(seed,1000,1000))&lt;br /&gt;   b_gpu = gpuPutArr(randomu(seed,1000,1000)) &lt;br /&gt;   c_gpu = gpuTranspose(a_gpu,LHS=a_gpu)##b_gpu  &lt;br /&gt;   c = gpuGetArr(c_gpu) &lt;br /&gt;   gpuFree,[a_gpu,b_gpu,c_gpu]&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;I'm now prettying up my CUDA-enabled ENVI extensions and thinking of new applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3227283900215751061?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3227283900215751061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3227283900215751061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3227283900215751061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3227283900215751061'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/11/gpulib-142.html' title='GPULib 1.4.2'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7689904000248878182</id><published>2010-10-23T11:33:00.020+02:00</published><updated>2010-10-26T13:14:51.694+02:00</updated><title type='text'>Singular Value Decomposition on the GPU</title><content type='html'>EM Photonics Inc. provide a cost-free basic version of  &lt;a href="http://www.culatools.com/"&gt;CULA Tools&lt;/a&gt;  which ports a number of  single-precision &lt;a href="http://en.wikipedia.org/wiki/LAPACK"&gt;LAPACK&lt;/a&gt; routines to &lt;a href="http://www.nvidia.com/object/cuda_home_new.html"&gt;CUDA&lt;/a&gt; and the GPU. One of these is &lt;a href="http://en.wikipedia.org/wiki/SVD_%28mathematics%29"&gt;SVD&lt;/a&gt; (singular value decomposition).  I wrapped it up into a DLM called CUDA_SVD which has similar functionality to IDL's LA_SVD procedure (without the DOUBLE keyword). On my old GeForce 8600GT graphics card I see a 7-fold speedup relative to LA_SVD for a 1000x2000 matrix, see the Figure below.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/TMKvyPkAWuI/AAAAAAAAApk/iIglD2kpuBs/s1600/speedups.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 229px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/TMKvyPkAWuI/AAAAAAAAApk/iIglD2kpuBs/s320/speedups.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5531176570070260450" /&gt;&lt;/a&gt;&lt;br /&gt;You can get the source together with the Windows compiled DLL &lt;a href="https://docs.google.com/leaf?id=0B7IDSHlM4SgEYTNmYjEzYjYtOGFjMi00Y2E1LWI2MDItZWUyYTM5N2U3ODI2&amp;hl=en"&gt;here&lt;/a&gt;. To run, you need a CUDA-enabled Nvidia GPU, its latest graphics driver, CUDA Toolkit 3.0 or later, the CULA 2.0 runtime and, of course, IDL.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7689904000248878182?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7689904000248878182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7689904000248878182' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7689904000248878182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7689904000248878182'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/10/singular-value-decomposition-on-gpu.html' title='Singular Value Decomposition on the GPU'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_-NwiSqQlzPY/TMKvyPkAWuI/AAAAAAAAApk/iIglD2kpuBs/s72-c/speedups.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-9183868745012037597</id><published>2010-08-28T18:14:00.003+02:00</published><updated>2010-08-28T21:22:36.122+02:00</updated><title type='text'>CUDA By Example</title><content type='html'>I just got hold of Sanders' and Kandrot's "&lt;a href="http://developer.nvidia.com/object/cuda-by-example.html"&gt;CUDA By Example&lt;/a&gt;".  It is a great introduction for beginners like me. A lot of fuzziness and confusion in my mind about what's going on has been blown away just reading the first few chapters. One small criticism: The authors state right at the beginning that CUDA C is a (relatively minor) extension of standard C. But in their examples they switch back and forth between C and C++ in a very cavalier manner. For instance, a code snippet given as part of an explanation may use C++ structures, whereas the final listing later in the chapter switches back to standard C. They even use C++ constructs in __device__ code. Is CUDA C is really CUDA C++? So I'm still a bit confused.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-9183868745012037597?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/9183868745012037597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=9183868745012037597' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/9183868745012037597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/9183868745012037597'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/08/cuda-by-example.html' title='CUDA By Example'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1632245821017852983</id><published>2010-08-06T16:16:00.003+02:00</published><updated>2010-08-06T16:43:51.413+02:00</updated><title type='text'>Cloud computing (another update)</title><content type='html'>Still no sign that Google will allow Numpy to be imported from Python scripts on their &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;App Engine&lt;/a&gt; servers, and hence permit efficient IDL/Matlab-like applications. Still, I find it a nice (and cheap) playground to learn web programming. I've added fast Fourier image transforms and frequency domain filtering to my App &lt;br /&gt;&lt;br /&gt;&lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;Image Analysis on the Google Cloud&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;and jazzed it up with a bit of Web 2.0 stuff, i.e., Ajax.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1632245821017852983?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1632245821017852983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1632245821017852983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1632245821017852983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1632245821017852983'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/08/cloud-computing-another-update.html' title='Cloud computing (another update)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7613575309254434108</id><published>2010-06-29T16:00:00.006+02:00</published><updated>2010-06-29T16:21:09.234+02:00</updated><title type='text'>Cloud computing (an update)</title><content type='html'>Well, now it's even possible to run IR-MAD (iterated MAD) on my &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;App&lt;/a&gt;. All you need is a bit of patience, see the &lt;a href="http://mortcanty-1.appspot.com/static/help.html#h12"&gt;Help Page&lt;/a&gt;. It has to be done this way because the App Server will time out after 30 seconds, and that's not long enough to iterate the algorithm automatically. It would be plenty of time if &lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=190&amp;colspec=ID%20Type%20Status%20Priority%20Stars%20Owner%20Summary%20Log%20Component"&gt;Numpy&lt;/a&gt; were available! (Are you listening, bosses of the Google App Engine?!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7613575309254434108?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7613575309254434108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7613575309254434108' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7613575309254434108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7613575309254434108'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/06/cloud-computing-update.html' title='Cloud computing (an update)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5079807133169354495</id><published>2010-06-19T13:08:00.006+02:00</published><updated>2010-06-19T15:00:15.580+02:00</updated><title type='text'>A GPU "gem" that didn't make it</title><content type='html'>I wrote a detailed description of my &lt;a href="http://www.txcorp.com/products/GPULib/"&gt;GPULib&lt;/a&gt; implementation of the &lt;a href="http://mcanty.homepage.t-online.de/idl/gpuKKmeans.zip"&gt;Kernel K-Means&lt;/a&gt; algorithm which didn't quite make the wire for the next edition of NVIDIA's "&lt;a href="http://www.nvidia.com/object/io_1265140268552.html"&gt;GPU Computing Gems&lt;/a&gt;". The reviewers' main objection was lack of detail regarding GPU computing considerations, e.g.,&lt;br /&gt;&lt;pre wrap=""&gt;&lt;span style="font-size:100%;"&gt;"data about how the algorithm behaves on the GPU such as # of coalesced memory reads &amp;amp; writes achieved, # branches, instructions/clock achieved, observed pcie bandwidth achieved with the mapping, whether the library achieved good occupancy or not ..."&lt;/span&gt;&lt;/pre&gt; This struck me as a bit silly, considering that GPULib is, among other things, just wrapping &lt;a href="http://www.nvidia.cn/content/cudazone/cuda_sdk/Linear_Algebra.html"&gt;CUBLAS&lt;/a&gt;, which in turn is written by NVIDIA's own software engineers. Anyway, here is the paper:&lt;br /&gt;&lt;a href="http://docs.google.com/fileview?id=0B7IDSHlM4SgEZGI2ZTZmMjMtYTU5Zi00YzM4LTgyYjgtODdjOTkxYjA0NjZh&amp;amp;hl=en"&gt;&lt;br /&gt;Kernel K-Means Clustering of Remote Sensing Imagery with CUDA&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5079807133169354495?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5079807133169354495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5079807133169354495' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5079807133169354495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5079807133169354495'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/06/gpu-gem-that-didnt-make-it.html' title='A GPU &quot;gem&quot; that didn&apos;t make it'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5889339930973673096</id><published>2010-05-13T15:16:00.013+02:00</published><updated>2010-05-13T16:09:26.838+02:00</updated><title type='text'>MAD on the Cloud (cont'd)</title><content type='html'>I must admit, this stuff gets addicting after a while. I now have a rather slick-looking interface to the Google App Engine application&lt;br /&gt;&lt;br /&gt;&lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;Image Analysis on the Google Cloud&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;which allows you to upload (small) multispectral images in about any format, view them, display their histograms, do various spectral transformations (PCA, NDVI, Tasseled Cap), perform change detection and/or radiometric normalization with MAD, and then download the results in ENVI standard format. &lt;br /&gt;&lt;br /&gt;Severe size restrictions on data structures and the lack of Python C-library support make it heavy going, but some things are maybe useful. For example, if you have ENVI EX or ENVI Runtime but no IDL (and hence no access to my programs), to radiometrically normalize spatial subsets of two co-registered scenes you can&lt;br /&gt;&lt;br /&gt;- upload them to the cloud in image slots 1 (reference image) and 2 (target image)&lt;br /&gt;- run MAD, putting the result in image slot 3&lt;br /&gt;- calculate a change probability image from the MAD image in image slot 4&lt;br /&gt;- run the normalization, overwriting image slot 2 with the normalized target&lt;br /&gt;- download the normalized image from image slot 2&lt;br /&gt;&lt;br /&gt;The screenshot below shows the radiometric normalization result page for two ASTER VNIR images. The regression coefficients could be used, eg, with ENVI's Band Math to normalize the full scenes.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/S-wATaJ0XLI/AAAAAAAAAmQ/PgQWKB1jGss/s1600/radcal.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 233px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/S-wATaJ0XLI/AAAAAAAAAmQ/PgQWKB1jGss/s320/radcal.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5470747980786326706" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This screenshot shows the reference image (center), the target image (left) and the normalized target image (right).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/S-wFHyL3vKI/AAAAAAAAAmY/PfItWc6_zSE/s1600/radcal1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 106px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/S-wFHyL3vKI/AAAAAAAAAmY/PfItWc6_zSE/s320/radcal1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5470753278637096098" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5889339930973673096?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5889339930973673096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5889339930973673096' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5889339930973673096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5889339930973673096'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/05/mad-on-cloud-contd.html' title='MAD on the Cloud (cont&apos;d)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_-NwiSqQlzPY/S-wATaJ0XLI/AAAAAAAAAmQ/PgQWKB1jGss/s72-c/radcal.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3127862505229680436</id><published>2010-04-05T15:12:00.006+02:00</published><updated>2010-04-05T16:06:56.875+02:00</updated><title type='text'>MAD on the Cloud</title><content type='html'>After "mucking about" over the Easter weekend, my &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;App Engine&lt;/a&gt; website &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;Image Analysis on the Google Cloud&lt;/a&gt; now includes change detection with the MAD transformation. &lt;br /&gt;&lt;br /&gt;Caveats:&lt;br /&gt;&lt;br /&gt;1. Because of Datastore restrictions, uploaded images must be 1MB or less. For instance, a 400x400 pixel 6-band LANDSAT image (= 960,000 bytes) will just fit. Using the &lt;a href="http://code.google.com/intl/de-DE/appengine/docs/python/blobstore/"&gt;Blobstore&lt;/a&gt; might fix this, but I'm not sure yet.&lt;br /&gt;&lt;br /&gt;2. This is not &lt;a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=4695"&gt;IR-MAD&lt;/a&gt;, just plain, ordinary, uniterated MAD. Iteration is my next project.&lt;br /&gt;&lt;br /&gt;3. You can view the result in RGB, but you can't download it. This will be fixed if and when I use the Blobstore (might cost money).&lt;br /&gt;&lt;br /&gt;4. CPU usage is high since everything is programmed in pure Python. This may cause the App Engine to throttle the service if the CPU quota (whatever it is) is exceeded:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/S7nqBCutY8I/AAAAAAAAAko/FeVAvI2WXYc/s1600/Current+CPU+load.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 130px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/S7nqBCutY8I/AAAAAAAAAko/FeVAvI2WXYc/s320/Current+CPU+load.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5456649727169618882" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Anyway, feedback always welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3127862505229680436?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3127862505229680436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3127862505229680436' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3127862505229680436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3127862505229680436'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/04/mad-on-cloud.html' title='MAD on the Cloud'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_-NwiSqQlzPY/S7nqBCutY8I/AAAAAAAAAko/FeVAvI2WXYc/s72-c/Current+CPU+load.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2188887052857683775</id><published>2010-04-02T10:13:00.004+02:00</published><updated>2010-04-02T10:47:37.643+02:00</updated><title type='text'>Python the hard way? (Cont'd)</title><content type='html'>My what a beautiful language. Here is a Python implementation of IDL's CHOLDC procedure for &lt;a href="http://en.wikipedia.org/wiki/Cholesky_decomposition"&gt;Cholesky decomposition&lt;/a&gt;:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;from paida.tools import Matrix&lt;br /&gt;from math import sqrt&lt;br /&gt;&lt;br /&gt;def choldc(A): &lt;br /&gt;# Cholesky-Banachiewicz algorithm&lt;br /&gt;    L = A - A &lt;br /&gt;    for i in range(len(L)):&lt;br /&gt;        for j in range(i):&lt;br /&gt;            sum = 0.0&lt;br /&gt;            for k in range(j):&lt;br /&gt;                sum += L[i,k]*L[j,k]&lt;br /&gt;            L[i,j] = (A[i,j]-sum)/L[j,j]&lt;br /&gt;        sum = 0.0&lt;br /&gt;        for k in range(i):&lt;br /&gt;            sum += L[i,k]*L[i,k]&lt;br /&gt;        L[i,i] = sqrt(A[i,i]-sum)        &lt;br /&gt;    return L     &lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;The parameter A is a &lt;a href="http://paida.sourceforge.net/"&gt;Paida&lt;/a&gt; Matrix instance for which +/- and * are matrix addition/subtraction and multiplication. If A is symmetric positive definite, the routine returns the unique lower triangular matrix L such that&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;A = L*L.transpose()  # in Python/Paida&lt;br /&gt;A = L##transpose(L)  ; in IDL&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;With this routine it is easy to solve the generalized eigenvalue problem which is needed to calculate MAD transformation coefficients. Whether or not all this will run on the &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;App Engine&lt;/a&gt; within my free CPU quotas remains to be seen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2188887052857683775?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2188887052857683775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2188887052857683775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2188887052857683775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2188887052857683775'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/04/python-hard-way-contd.html' title='Python the hard way? (Cont&apos;d)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3337498359905932164</id><published>2010-03-18T21:07:00.004+01:00</published><updated>2010-03-18T22:07:22.748+01:00</updated><title type='text'>Python the hard way? (Cont'd)</title><content type='html'>OK, mucking about with Python on the &lt;a href="http://code.google.com/intl/de-DE/appengine/"&gt;Google App Engine&lt;/a&gt; is a bit of fun after all. I discovered, next to &lt;a href="http://packages.python.org/pypng/ex.html"&gt;PyPng&lt;/a&gt;, another pure Python toolbox called &lt;a href="http://paida.sourceforge.net/"&gt;PAIDA&lt;/a&gt;, which has a matrix object, can solve eigenvalue problems and implements &lt;a href="http://en.wikipedia.org/wiki/BLAS"&gt;BLAS&lt;/a&gt; (Basic Linear Algebra Subprograms). Also I fugured out how to upload and store not just ENVI images, but JPEG, PNG, GIF, TIFF, BMP and ICO in ENVI standard format, see the &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;App&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I discovered in the process that there are some very nasty differences between the App Engine SDK and the App Engine proper (i.e. Google's Cloud). &lt;br /&gt;&lt;br /&gt;For example, there is a memory limit on the App Engine, but none on the SDK (others have remarked with dismay on this). So you get a nice application running on your own machine with the SDK, deploy it and ... April Fool!&lt;br /&gt;&lt;br /&gt;Here's a subtle one: when you convert, say, a JPEG to a PNG with App Engine's Image class, it hangs an &lt;a href="http://en.wikipedia.org/wiki/Alpha_channel"&gt;alpha channel&lt;/a&gt; onto the result. When you do it with the SDK's Image class, it doesn't.&lt;br /&gt;&lt;br /&gt;Next thing on the agenda: principal components analysis. Then, if I can use PAIDA to write a pure Python routine for &lt;a href="http://en.wikipedia.org/wiki/Cholesky_decomposition"&gt;Cholesky decomposition&lt;/a&gt;, I'll try to implement the &lt;a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=4695"&gt;MAD transformation&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3337498359905932164?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3337498359905932164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3337498359905932164' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3337498359905932164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3337498359905932164'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/03/python-hard-way-contd_18.html' title='Python the hard way? (Cont&apos;d)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6972858959964434088</id><published>2010-03-15T20:50:00.004+01:00</published><updated>2010-03-15T22:16:47.268+01:00</updated><title type='text'>Python the hard way? (Cont'd)</title><content type='html'>Maybe I should start a new Blog: &lt;span style="font-weight: bold;"&gt;Fun With Python/Google App Engine&lt;/span&gt;. Except ... it's not fun. I thought I could at least play around with the &lt;a class="zem_slink" href="http://www.pythonware.com/products/pil/" title="Python Imaging Library" rel="homepage"&gt;PIL&lt;/a&gt; on my App. But the App Engine clips the PIL's wings so severely that about all you can do is make thumbnails. Cool.&lt;br /&gt;&lt;br /&gt;Then there's the datastore. It supplies the only persistence, since you're in a sandbox and can't write to the App server's file system. It allows the user to store and retrieve a maximum of 1 MB at a time. Cooler.&lt;br /&gt;&lt;br /&gt;Only pure Python modules can be used. So no Numpy, no Scipy, no Matplotlib. Coolest.&lt;br /&gt;&lt;br /&gt;Fortunately, &lt;a href="http://code.google.com/p/pypng/"&gt;PyPng&lt;/a&gt; is written in pure Python, so I included it in my &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;deployed app&lt;/a&gt;. I can take an uploaded ENVI image from the datastore and, after doing something trivial like calculating an NDVI, serve the result as an image. Can't for the life of me figure out how to insert the &lt;a class="zem_slink" href="http://en.wikipedia.org/wiki/Portable_Network_Graphics" title="Portable Network Graphics" rel="wikipedia"&gt;PNG&lt;/a&gt; image into an HTML document, though. But it must be possible.&lt;br /&gt;&lt;br /&gt;I guess scientific computing on Google's cloud has a long way to go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6972858959964434088?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6972858959964434088/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6972858959964434088' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6972858959964434088'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6972858959964434088'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/03/python-hard-way-contd.html' title='Python the hard way? (Cont&apos;d)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4465066424648384830</id><published>2010-03-10T18:03:00.007+01:00</published><updated>2010-03-12T20:35:07.870+01:00</updated><title type='text'>Python the hard way?</title><content type='html'>Most introductory books I've seen on Python emphasize how easy it is to learn, and then attempt to illustrate that fact in the next 400 to 1000 pages. OK, Python is elegant and great to work with. But my experience is that, when learning any new computer language, it is always best to have a concrete, nontrivial task in mind and then just dive into it. &lt;br /&gt;&lt;br /&gt;So I'm diving into the &lt;a href="http://code.google.com/intl/de-DE/appengine/docs/python/overview.html"&gt;Python API &lt;/a&gt;for the Google App Engine (see an &lt;a href="http://fwenvi-idl.blogspot.com/2009/11/cloudy-thoughts.html"&gt;earlier Blog&lt;/a&gt;), never having before programmed a single line of Python in my life. Here you can watch me &lt;a href="http://mortcanty-1.appspot.com/static/index.html"&gt;mucking about&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4465066424648384830?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4465066424648384830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4465066424648384830' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4465066424648384830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4465066424648384830'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/03/python-hard-way.html' title='Python the hard way?'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1143923040364257901</id><published>2010-02-06T13:14:00.012+01:00</published><updated>2010-02-06T13:47:19.687+01:00</updated><title type='text'>Kernel PCA</title><content type='html'>In a paper on kernel principal component analysis (KPCA) (Kim et al., Transactions on Pattern Analysis and Machine Intelligence 27(9) 2005, 1351-1366) it is stated:&lt;br /&gt;&lt;br /&gt;"In contrast to linear PCA, KPCA is capable of capturing part of the higher-order statistics which are particularly important for encoding image structure."&lt;br /&gt;&lt;br /&gt;I offer a (CUDA-enabled) script for doing kernel PCA on my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;software page&lt;/a&gt;. Is it useful for remote sensing image analysis? Here is band 3 (near infrared) of an ASTER image, as usual over my home town Juelich:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/S21gwDX914I/AAAAAAAAAic/PSlUZ8GPKyc/s1600-h/juelich_sept_30_band3.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/S21gwDX914I/AAAAAAAAAic/PSlUZ8GPKyc/s320/juelich_sept_30_band3.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5435106703961872258" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I ran a kernel principal component analysis on all three visual/near infrared bands. Here is the fourth kernel principal component:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/S21hkG-tJNI/AAAAAAAAAik/l9qXitc-khA/s1600-h/juelich_sept_30_kpc4.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/S21hkG-tJNI/AAAAAAAAAik/l9qXitc-khA/s320/juelich_sept_30_kpc4.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5435107598282859730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you click on these two images to enlarge them, you will see that KPCA can act as a pretty good edge detector, something that ordinary PCA certainly can't.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1143923040364257901?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1143923040364257901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1143923040364257901' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1143923040364257901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1143923040364257901'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/02/kernel-pca.html' title='Kernel PCA'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_-NwiSqQlzPY/S21gwDX914I/AAAAAAAAAic/PSlUZ8GPKyc/s72-c/juelich_sept_30_band3.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-144798217403116151</id><published>2010-01-14T21:35:00.017+01:00</published><updated>2010-01-15T13:19:39.316+01:00</updated><title type='text'>Learning CUDA</title><content type='html'>I've been experimenting a lot with &lt;a href="http://www.txcorp.com/products/GPULib/"&gt;GPULib&lt;/a&gt; (see many earlier blogs), a library which shields you nicely from the details of &lt;a href="http://www.nvidia.com/object/cuda_home.html#"&gt;CUDA programming&lt;/a&gt;. But to learn more about what goes on behind the scenes, I decided to try my hand at writing my own CUDA DLM for IDL. The function is pretty pointless: it does image contrast stretching on the GPU with a pre-calculated lookup table, something that plain IDL does very efficiently anyway. But it was a start. &lt;br /&gt;&lt;br /&gt;For example it was interesting to time what's going on. The function, called CUDA_STRETCH(LUT, IMG), takes a 256-byte lookup table LUT and an image band IMG as input and returns the stretched result. So both arrays have to be passed to and from the GPU each time. Here is the calling program:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;pro STRETCH&lt;br /&gt;   envi_select, title='Choose MS image band', $&lt;br /&gt;        fid=fid, dims=dims, pos=pos, /band_only&lt;br /&gt;   if (fid eq -1) then return&lt;br /&gt;   img = bytscl(envi_get_data(fid=fid,dims=dims,pos=pos))&lt;br /&gt;   h = histogram(img,min=0,max=255,nbins=256)    &lt;br /&gt;; make a histogram equalization lookup table      &lt;br /&gt;   LUT = byte(255*(total(h, /cumulative)/total(h)))&lt;br /&gt;; call the CUDA DLM once to get it loaded&lt;br /&gt;   imgout = cuda_stretch(LUT, img) &lt;br /&gt;; now time it   &lt;br /&gt;   start = systime(2)&lt;br /&gt;   imgout = cuda_stretch(LUT, img)   &lt;br /&gt;   print, 'CUDA: ',systime(2)-start   &lt;br /&gt;; return result to ENVI   &lt;br /&gt;   envi_enter_data, imgout&lt;br /&gt;; now time the IDL version   &lt;br /&gt;   start = systime(2)   &lt;br /&gt;   imgout = LUT[img]&lt;br /&gt;   print, 'IDL: ',systime(2)-start  &lt;br /&gt;end&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;The console output for a 4000x4000 image is&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;CUDA:       0.15700006&lt;br /&gt;IDL:      0.015000105&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;(I said it was pointless.) My first thought was that most of the overhead was due to host - device transfers, but here is what the internal timer in the DLM says:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;cuda_stretch ---------------------&lt;br /&gt;clocks per second 1000&lt;br /&gt;transferring arrays to device ...&lt;br /&gt;cols = 4000 rows = 4000&lt;br /&gt;clocks required   15&lt;br /&gt;launching kernel ...&lt;br /&gt;kernel done ...&lt;br /&gt;clocks required   110&lt;br /&gt;transferring result to host ...&lt;br /&gt;clocks required   15&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;So the kernel is the problem, and I guess it is just programmed too naively.&lt;br /&gt;&lt;br /&gt;Anyone else who would like to experiment along these lines can get the C code, DLM description file, Visual Studio 2008 project file, etc., etc &lt;a href="http://mcanty.homepage.t-online.de/stretch.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-144798217403116151?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/144798217403116151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=144798217403116151' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/144798217403116151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/144798217403116151'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2010/01/learning-cuda.html' title='Learning CUDA'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3250251209382862188</id><published>2009-12-06T15:27:00.012+01:00</published><updated>2009-12-06T18:50:58.395+01:00</updated><title type='text'>The Madman</title><content type='html'>My &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;book&lt;/a&gt; documents the ENVI/IDL extensions for MAD change detection and radiometric normalization pretty thoroughly, but after answering several emails with the same or similar questions from users, I thought either a short manual or a Frequently Asked Questions compilation might be in order. I opted for the manual because, to my childlike (childish?) brain, &lt;a href="http://docs.google.com/fileview?id=0B7IDSHlM4SgEMzYzOTQ1YTAtY2ZmYS00ZjI2LWIyZWQtNTU2ZWYyNzMwNDQy&amp;hl=en"&gt;MAD MAN&lt;/a&gt; sounded cooler than MAD FAQ. &lt;br /&gt;&lt;br /&gt;I hope this helps, as they say on the newsgroups.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3250251209382862188?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3250251209382862188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3250251209382862188' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3250251209382862188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3250251209382862188'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/12/madman.html' title='The Madman'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6894720606486928817</id><published>2009-11-18T18:37:00.007+01:00</published><updated>2009-11-18T19:01:49.151+01:00</updated><title type='text'>GPULib 1.2.2</title><content type='html'>Just downloaded and installed the &lt;a href="http://gpulib.blogspot.com/2009/11/gpulib-122-released.html"&gt;latest version&lt;/a&gt; of GPULib from the &lt;a href="http://www.txcorp.com/products/GPULib/"&gt;Tech-X website&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I noticed in their announcement that they have "added several missing Windows build files." This caught my interest, as I haven't yet been able to get a build to work on my Windows machine. &lt;br /&gt;&lt;br /&gt;So I loaded the Visual C++ solution file provided in the distribution into Visual C++ 2008 Express Edition and let the Wizard convert it from 2005 to 2008 format. &lt;br /&gt;&lt;br /&gt;The first build attempt complained about not finding idl_export.h and idl.lib. After copying these from my IDL installation to the gpulib-1.2.2\idl directory, Lo and Behold -- it built!&lt;br /&gt;&lt;br /&gt;However, I got 466 warning messages, so I didn't really believe it until I replaced the distributed DLL with the newly built one. All of my IDL/GPULib programs run as before. Very nice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6894720606486928817?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6894720606486928817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6894720606486928817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6894720606486928817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6894720606486928817'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/11/gpulib-122.html' title='GPULib 1.2.2'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5997959835521940667</id><published>2009-11-09T19:00:00.012+01:00</published><updated>2009-11-09T20:06:33.903+01:00</updated><title type='text'>Cloudy thoughts</title><content type='html'>Browsing (physically) through a book store a few days ago I came across Charles Severance's &lt;a href="http://oreilly.com/catalog/9780596801601/"&gt;Using Google App Engine&lt;/a&gt;. I took it home and devoured it in one evening. It's absolutely fascinating what Google allows you to do on the web for free. I downloaded the App Engine simulator to play around with and started dreaming of putting MAD/RADCAL on the "cloud", programmed in &lt;a href="http://en.wikipedia.org/wiki/Python_%28programming_language%29"&gt;Python&lt;/a&gt;. Of course number crunching is not exactly what Google App Engine is for, but it would be a great platform for demonstration. Small images wouldn't use much CPU time.&lt;br /&gt;&lt;br /&gt;Anyway, I sobered up pretty quickly when I learned that &lt;a href="http://en.wikipedia.org/wiki/NumPy"&gt;NumPy&lt;/a&gt; (the Python extension that allows IDL/Matlab-style programming) isn't supported. A lot of users are asking for NumPy support in the Google forums but, if I understand correctly, its reliance on C-libraries more or less disqualifies it from deployment on the Google infrastucture (the "cloud"). But maybe I'm wrong?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5997959835521940667?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5997959835521940667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5997959835521940667' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5997959835521940667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5997959835521940667'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/11/cloudy-thoughts.html' title='Cloudy thoughts'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4636292063715335977</id><published>2009-11-02T18:51:00.028+01:00</published><updated>2009-11-02T21:28:55.978+01:00</updated><title type='text'>Radiometric normalization revisited</title><content type='html'>Judging from the emails I've received lately, quite a few people are using the ENVI/IDL extension RADCAL.PRO for radiometric normalization. In an earlier blog I gave an example, taken from &lt;a href="http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=5362"&gt;Canty and Nielsen (2008)&lt;/a&gt;, where the method gave a nice result. Now I'll show a situation (from the same paper) where it fails rather miserably, and then indicate how it might be made to work after all.&lt;br /&gt;&lt;br /&gt;The example again involves two LANDSAT 7 ETM+ images acquired over Juelich, Germany, this time in May and late August of the same year. The amount of vegetation change in the scene is extreme, so that when IR-MAD is run on the six non-thermal LANDSAT bands it does not converge to a well-defined background of no-change pixels:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/Su8nE3zPHnI/AAAAAAAAAfs/9D-fCELTQ8k/s1600-h/bild1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/Su8nE3zPHnI/AAAAAAAAAfs/9D-fCELTQ8k/s320/bild1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5399577442892324466" /&gt;&lt;/a&gt;&lt;br /&gt;The above is an RGB composite of MAD bands 4,5 and 6. It is very noisy, and a clean no-change background is difficult to make out. Using the associated Chi-Square image with RADCAL to perform automatic normalization doesn't lead to a reasonable result. Here for example is the regression line for band 5:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8q_ZouR-I/AAAAAAAAAf0/Gs9UTPFwbLk/s1600-h/Bild3.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 256px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8q_ZouR-I/AAAAAAAAAf0/Gs9UTPFwbLk/s320/Bild3.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5399581746942330850" /&gt;&lt;/a&gt;&lt;br /&gt;The test statistics from RADCAL confirm the failure. The p-values for the F-test for equal variances of the reference and normalized target images are zero or very small for all six bands (Blogger is truncating the table at three bands, but you get the idea):&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;RADCAL statistics: 4245 training and 2123 test pixels   &lt;br /&gt;Variances  &lt;br /&gt;target         165.2327    297.3767    895.3079    401.0657    991.1505   1274.5616  &lt;br /&gt;reference       76.3946    138.7130    381.9422    347.6269    410.2062    399.8563  &lt;br /&gt;normalized      28.5304     43.2249     16.4233    301.8247     56.1722      1.2324  &lt;br /&gt;F-stat           2.6777      3.2091     23.2562      1.1518      7.3027    324.4514  &lt;br /&gt;p-value          0.0000      0.0000      0.0000      0.0011      0.0000      0.0000  &lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;But now we'll choose a spatial subset of the images, centered on one of the mining areas, where we might expect to find a higher fraction of invariant pixels and therefore hope for better convergence. The IR-MAD image certainly looks much cleaner:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8txqh9OSI/AAAAAAAAAf8/9zdlbG3d8cE/s1600-h/Bild4.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8txqh9OSI/AAAAAAAAAf8/9zdlbG3d8cE/s320/Bild4.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5399584809494067490" /&gt;&lt;/a&gt;&lt;br /&gt;We're also encouraged by the fact that the no-change pixels (middle gray) correspond both to forest canopy and to built-up areas. Running RADCAL again, this time on the chosen subset, we get good regression lines. For instance for Band 5:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8uTUlyJUI/AAAAAAAAAgE/sA0eehlaiXI/s1600-h/Bild6.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 256px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/Su8uTUlyJUI/AAAAAAAAAgE/sA0eehlaiXI/s320/Bild6.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5399585387720090946" /&gt;&lt;/a&gt;&lt;br /&gt;and the statistical test also tells us that we can't reject the hypothesis of equal variances for the reference and normalized images:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;RADCAL statistics: 453 training and 227 test pixels&lt;br /&gt;Variances  &lt;br /&gt;target          45.7903     69.3390    182.2367    435.2744    487.0745    352.9176  &lt;br /&gt;reference       32.0876     49.1145    122.2010    139.4574    270.9004    197.6096  &lt;br /&gt;normalized      34.9058     53.8019    131.8851    137.8543    275.6107    203.5141  &lt;br /&gt;F-stat           1.0878      1.0954      1.0792      1.0116      1.0174      1.0299  &lt;br /&gt;p-value          0.5274      0.4938      0.5670      0.9308      0.8970      0.8251 &lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;So now we can tell RADCAL to use the regression coefficients obtained from the spatial subset to calibrate the entire scene, and we may have a useful normalization after all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4636292063715335977?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4636292063715335977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4636292063715335977' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4636292063715335977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4636292063715335977'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/11/radiometric-normalization-revisited.html' title='Radiometric normalization revisited'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_-NwiSqQlzPY/Su8nE3zPHnI/AAAAAAAAAfs/9D-fCELTQ8k/s72-c/bild1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4800319480066579313</id><published>2009-10-08T21:08:00.015+02:00</published><updated>2009-10-08T22:23:42.120+02:00</updated><title type='text'>Shifting the mean</title><content type='html'>One of the subjects that I take up in the &lt;a href="http://www.crcpress.com/product/isbn/9781420087130"&gt;second edition of my book&lt;/a&gt; is image segmentation. A very popular segmetation algorithm is the so-called &lt;a href="http://en.wikipedia.org/wiki/Mean-shift"&gt;mean shift&lt;/a&gt; and it works like this: Suppose we want to segment an image like the one below, which consists of the first 3 principal components of a 9-band ASTER image: &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_-NwiSqQlzPY/Ss4-kZYQ7zI/AAAAAAAAAd0/svZDVLeDpF0/s1600-h/Bild1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_-NwiSqQlzPY/Ss4-kZYQ7zI/AAAAAAAAAd0/svZDVLeDpF0/s320/Bild1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5390314599017934642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Take all of the 3-dimensional pixel vectors and extend them to 5-dimensional vectors by adding their x and y spatial coordinates, normalized to the 3 spectral intensity coordinates in some convenient way. Choose any pixel and, in the 5-dimensional feature space, center a hypersphere of radius r on that pixel. Calculate the (5-dimensional) mean of all of the pixels in the hypersphere and then shift its center to that mean. Then recalculate the mean and shift again. Keep doing this until convergence. The point of convergence is called a "mode", a local maximum in the density distribution of the pixels in the feature space. Replace the spectral components of the original pixel with those of the mode but retain the pixel's original spatial coordinates. Now do the same for all of the other pixels in the image. The result is a segmented image:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/Ss5AjyugF9I/AAAAAAAAAd8/FHaz9ManF4M/s1600-h/Bild2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/Ss5AjyugF9I/AAAAAAAAAd8/FHaz9ManF4M/s320/Bild2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5390316787665475538" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are about 740 modes or segments in the above picture, the spatial coordinates of the modes being roughly at the segment centroids. The positions of the modes in the spectral subspace look like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-NwiSqQlzPY/Ss5C7S1NGcI/AAAAAAAAAeE/TZ4WTM3_u2I/s1600-h/Bild4.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 297px; height: 320px;" src="http://3.bp.blogspot.com/_-NwiSqQlzPY/Ss5C7S1NGcI/AAAAAAAAAeE/TZ4WTM3_u2I/s320/Bild4.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5390319390443772354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here I've clustered them for fun, using 5 classes. If we re-lable the segments with the corresponding class color, we get - in a roundabout way - a rather nice unsupervised classification:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-NwiSqQlzPY/Ss5D4FTyPzI/AAAAAAAAAeM/mSaFPY-ZwHc/s1600-h/Bild5.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_-NwiSqQlzPY/Ss5D4FTyPzI/AAAAAAAAAeM/mSaFPY-ZwHc/s320/Bild5.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5390320434785959730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here's a blow-up of a spatial subset of the above with the original segment boundaries overlayed:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/Ss5Gd1dgC9I/AAAAAAAAAeU/qSWr7VpemEQ/s1600-h/Bild6.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 304px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/Ss5Gd1dgC9I/AAAAAAAAAeU/qSWr7VpemEQ/s320/Bild6.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5390323282390027218" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4800319480066579313?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4800319480066579313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4800319480066579313' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4800319480066579313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4800319480066579313'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/10/shifting-mean.html' title='Shifting the mean'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_-NwiSqQlzPY/Ss4-kZYQ7zI/AAAAAAAAAd0/svZDVLeDpF0/s72-c/Bild1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6420531720978931384</id><published>2009-09-28T10:55:00.012+02:00</published><updated>2009-09-28T14:39:19.260+02:00</updated><title type='text'>Clustering changes</title><content type='html'>I've discussed the generation of multi-band change images with the &lt;a href="http://www2.imm.dtu.dk/pubdb/p.php?4695"&gt;iterated MAD method&lt;/a&gt; and, more recently, the clustering of multispectral images with a Gaussian mixture model. So why not combine the two?  Here's an example:&lt;br /&gt;&lt;br /&gt;The image below is an RGB composite of three of six MAD components showing changes in the region around Juelich, Germany, where I live. The MAD components were derived from two Landsat 7 ETM scenes acquired in the months of June and August, 2001. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_-NwiSqQlzPY/SsB8kMMHFLI/AAAAAAAAAdc/r11o_7gIQZY/s1600-h/irmad.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_-NwiSqQlzPY/SsB8kMMHFLI/AAAAAAAAAdc/r11o_7gIQZY/s320/irmad.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5386442115524269234" /&gt;&lt;/a&gt;&lt;br /&gt;The blue-gray (featureless) regions correspond to built-up areas and to forest canopy, neither of which have changed significantly between acquisitions. The significant changes, indicated by the various colored areas, are quite heterogeneous and not easy to interpret, but from their form they are clearly associated with cultivated fields. The main crops around here are sugar beets, corn and cereal grain. The former two are still maturing in August, whereas the grain fields have been harvested. So we might postulate two main classes of change: maturing crops and harvested crops. Allowing for a no-change class and a "catch-all" class for other kinds of change, we can try to classify the MAD image with the Guassian mixture clustering algorithm by assuming four classes in all. The resulting clusters, in the feature space of the three MAD components, are shown below with to help of ENVI's n-d visualiser.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/SsB_XX2hgXI/AAAAAAAAAdk/HIta2j5b97U/s1600-h/ndv.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 278px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/SsB_XX2hgXI/AAAAAAAAAdk/HIta2j5b97U/s320/ndv.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5386445193851535730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are three well-defined clusters (the central (white) one is no change) and a diffuse cluster (blue). Identifying the red and green classes as the two postulated ones and overlaying them onto band 4 of the August Landsat image we get: &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_-NwiSqQlzPY/SsCOmOwfnZI/AAAAAAAAAds/FbGtUdo_LQM/s1600-h/em.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_-NwiSqQlzPY/SsCOmOwfnZI/AAAAAAAAAds/FbGtUdo_LQM/s320/em.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5386461941782781330" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The red regions correspond to low reflectance in band 4 (near infrared) and therefore (presumably) to harvested grain, the green regions (presumably) to maturing crops.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6420531720978931384?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6420531720978931384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6420531720978931384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6420531720978931384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6420531720978931384'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/09/clustering-changes.html' title='Clustering changes'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_-NwiSqQlzPY/SsB8kMMHFLI/AAAAAAAAAdc/r11o_7gIQZY/s72-c/irmad.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-619089997138920922</id><published>2009-09-23T14:58:00.005+02:00</published><updated>2009-09-23T16:35:31.916+02:00</updated><title type='text'>Confessions of a "no-C" programmer</title><content type='html'>I wrote my first computer program way back in 1961 in a weird machine language on an even weirder machine called, as I recall,  the Bendix G15D. From thence I graduated to Fortran on an IBM 1620 and, over many subsequent years, made sacrificial offerings of boxes upon boxes of punched cards to ever larger and hungrier IBM mainframes. &lt;br /&gt;&lt;br /&gt;But then a wonderful/horrible invention called the IBM PC suddenly appeared on the scene. So I had to learn to use BASIC for a while, followed by a love affair with Turbo Pascal which continued up until about Delphi 3.0. After two illicit affairs with Prolog and Mathematica I finally landed in the here and now with good old IDL.&lt;br /&gt;&lt;br /&gt;Something essential is missing, right? Yes, over all these years I have actually succeeded &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; to learn how to use C (or its derivatives C++ and Java). So why am I confessing this shameful fact? Because, at 66, I'm living proof that "you're never too old to learn". I proudly announce that I've just succeeded in &lt;span style="font-style:italic;"&gt;writing my very first DLM &lt;/span&gt; for IDL, using IDL_VPTR's, stuff like "X-&gt;value.arr-&gt;data", the whole, cryptic lot! &lt;br /&gt;&lt;br /&gt;The DLM replaces the old CALL_EXTERNAL() DLL for the provisional means algorithm which is used in the MAD program to collect image statistics. I haven't put it on my website along with a version of MAD which uses it yet, because experience shows that that can cause no end of confusion (see earlier blog). I'll think of something.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-619089997138920922?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/619089997138920922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=619089997138920922' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/619089997138920922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/619089997138920922'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/09/confessions-of-no-c-programmer.html' title='Confessions of a &quot;no-C&quot; programmer'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-8426308860683179695</id><published>2009-09-08T16:37:00.024+02:00</published><updated>2009-09-10T16:16:01.715+02:00</updated><title type='text'>Clustering Images on the GPU</title><content type='html'>Well, I seem at last to have a version of Gaussian mixture clustering working with GPULib. Unfortunately, it crashes my system when I run it on large images (&amp;gt; around 1000x1000 pixels) because of limitations on the GPU memory. It gives a speedup factor of about four  relative to the original CPU version described in Chapter 7 of my &lt;a href="http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=7251&amp;parent_id=&amp;pc="&gt;book&lt;/a&gt; (same as for the neural net I talked dabout a few blogs ago). Among some other cool features, the algorithm uses multiresolution image pyramids created with the discrete wavelet transformation to speed up clustering.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-NwiSqQlzPY/SqZu5XaVsqI/AAAAAAAAAck/x2KHjQJ9TGQ/s1600-h/em1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 106px;" src="http://3.bp.blogspot.com/_-NwiSqQlzPY/SqZu5XaVsqI/AAAAAAAAAck/x2KHjQJ9TGQ/s320/em1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5379108736756724386" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Click on the image above to see successive stages for a 3-level pyramid created from a Landsat 7 ETM image. The red cluster is mixed forest. It becomes more sharply defined from left (120m resolution) to right (30m resolution). Clustering only at the native 30m resolution (no pyramid) takes five times longer.&lt;br /&gt; &lt;br /&gt;You can get the routine &lt;a href="http://mcanty.homepage.t-online.de/idl/gpuem.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-8426308860683179695?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/8426308860683179695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=8426308860683179695' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8426308860683179695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/8426308860683179695'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/09/clustering-images-on-gpu.html' title='Clustering Images on the GPU'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_-NwiSqQlzPY/SqZu5XaVsqI/AAAAAAAAAck/x2KHjQJ9TGQ/s72-c/em1.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4390495702128209904</id><published>2009-08-23T15:45:00.004+02:00</published><updated>2009-08-23T16:02:15.506+02:00</updated><title type='text'>Are you MAD because CHOLDC failed?</title><content type='html'>Been getting several emails from users of my ENVI/IDL extension for MAD, saying that they are getting the message "CHOLDC failed". The reason is that I updated the external routine PROV_MEANS.DLL some while back. The &lt;a href="http://mcanty.homepage.t-online.de/idl/mad.zip"&gt;current version&lt;/a&gt; of the MAD program also requires the &lt;a href="http://mcanty.homepage.t-online.de/idl/auxil.zip"&gt;current version&lt;/a&gt; of the DLL. If the old DLL is anywhere on the OS path, it still may be picked up, so be sure to eradicate it completely when installing the new version. Humble apologies to all affected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4390495702128209904?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4390495702128209904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4390495702128209904' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4390495702128209904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4390495702128209904'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/08/are-you-mad-because-choldc-failed.html' title='Are you MAD because CHOLDC failed?'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4311648782428696064</id><published>2009-07-16T14:43:00.006+02:00</published><updated>2009-07-16T15:49:58.816+02:00</updated><title type='text'>GPULib 1.2</title><content type='html'>I downloaded and installed the latest version of GPULib on my windows machine. It gave me wierd results at first, till I realized that I had to point the DLM path to&lt;br /&gt;&lt;br /&gt;\gpulib-1.2\bindist\idl\x86\single&lt;br /&gt;&lt;br /&gt;as my antiquated nvidia card doesn't support double precision. &lt;br /&gt;&lt;br /&gt;Version 1.2 caters mostly to Matlab, see the &lt;a href="http://gpulib.blogspot.com/"&gt;Tech-X blog&lt;/a&gt;. But some bugs in IDL have also been fixed, in particular the dimension parameter in GPUTOTAL() which was giving me headaches.&lt;br /&gt;&lt;br /&gt;For anyone else who might be developing ENVI extensions with GPULib, I find the following little program to be useful when placed in SAVE_ADD:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;PRO aaa_run_define_buttons, buttonInfo&lt;br /&gt;   ENVI_DEFINE_MENU_BUTTON, buttonInfo, $&lt;br /&gt;      VALUE = 'GPULib API', $&lt;br /&gt;      REF_VALUE = 'About ENVI', $&lt;br /&gt;      EVENT_PRO = 'aaa_run', $&lt;br /&gt;      UVALUE = 'AAA',$&lt;br /&gt;      POSITION = 'after' &lt;br /&gt;   gpuinit &lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;PRO aaa_run,event&lt;br /&gt;   mg_open_url, $&lt;br /&gt;   'http://www.txcorp.com/products/GPULib/'+ $&lt;br /&gt;    'idl_docs/index.html' &lt;br /&gt;END&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;ENVI picks it up first (I think it scans the SAVE_ADD directory alphabetically) and runs the define_buttons procedure to place a link to the GPULib API in the help menu. (You can get MG_OPEN_URL from &lt;a href="http://michaelgalloy.com/"&gt;Mike Galloy&lt;/a&gt;.) As a side effect, GPUINIT is called so that any other GPULib code in SAVE_ADD will compile properly. Maybe it only works on windows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4311648782428696064?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4311648782428696064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4311648782428696064' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4311648782428696064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4311648782428696064'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/07/gpulib-12.html' title='GPULib 1.2'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2952298247043132311</id><published>2009-07-07T20:43:00.023+02:00</published><updated>2009-07-10T13:25:36.600+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='radiometric normalization'/><category scheme='http://www.blogger.com/atom/ns#' term='change detection'/><category scheme='http://www.blogger.com/atom/ns#' term='MAD'/><title type='text'>Normalizing Images</title><content type='html'>I said in my previous blog that the &lt;a href="http://www2.imm.dtu.dk/pubdb/p.php?4695"&gt;MAD&lt;/a&gt; components are insensitive to uninteresting effects like solar illumination, differing sensor gains or atmospheric absorption. In fact, if the "uninteresting effects" are linear in nature, then the MAD components are &lt;span style="font-style: italic;"&gt;strictly invariant&lt;/span&gt;. &lt;br /&gt;&lt;small&gt;&lt;hr size="1" noshade="noshade"&gt; Mathematically, let X be any pixel vector in an ASTER VNIR image at time T1 and Y any pixel vector in a second image taken at a later time T2. These vectors each have three components corresponding to the three spectral bands. Suppose that A and B are two arbitrary 3x3 matrices and that C and D are two equally arbitrary 3-component vectors. Now look at the linear transformations X' = AX+C, and Y' = BY+D. Performing the MAD procedure on X' and Y' gives &lt;span style="font-style: italic;"&gt;exactly&lt;/span&gt; the same result as performing it on X and Y!&lt;br /&gt;&lt;/small&gt;&lt;hr size="1" noshade="noshade"&gt; What this means is that the MAD algorithm will find the same change pixels and, by implication, the same &lt;span style="font-style: italic;"&gt;no-change&lt;/span&gt; pixels irrespective of any linear perturbations, attenuations or shifts of the image data.&lt;br /&gt;&lt;br /&gt;Suppose one image is acquired under "favorable" conditions, e.g., high solar elevation, clear visibility, optimal sensor gain, and the second image under poorer conditions. Then it would be nice to &lt;span style="font-style: italic;"&gt;radiometrically normalize&lt;/span&gt; the second image to the first one. Then one could, for example, compare vegetation indices in the two images in a meaningful way. To do the normalization properly, we need to base it on the no-change pixels. Including pixels which correspond to real, phyisical changes on the ground would invalidate the normalization. The MAD algorithm will provide us with the no-change pixels.&lt;br /&gt;&lt;br /&gt;An example: The two ASTER VNIR images below were acquired over an area east of Esfahan, Iran in April 2001 (left) and September 2005 (right). The pixel intensities are displayed as digital numbers in the range 0-255 and are proportional to the radiance measured at the satellite sensor. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/SlX2nPd5EUI/AAAAAAAAAYk/VQsCIMdBQIM/s1600-h/iran1.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 200px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/SlX2nPd5EUI/AAAAAAAAAYk/VQsCIMdBQIM/s400/iran1.jpg" alt="" id="BLOGGER_PHOTO_ID_5356458485854310722" border="0"&gt;&lt;/a&gt;&lt;br /&gt;There's alot of cloud cover in the left hand image and big differences in vegetation cover (indicated by the red pixels). We'll normalize the second image to the first image. After running MAD to obtain the pixels with highest probability of no change (greater than 90% in this example), a band-by-band &lt;a href="http://en.wikipedia.org/wiki/Orthogonal_regression"&gt;orthogonal linear regression&lt;/a&gt; of the first (reference) image against the second (target) image is performed:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/SlX2PdUHpWI/AAAAAAAAAYc/cUKAstZVW94/s1600-h/iran3.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 107px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/SlX2PdUHpWI/AAAAAAAAAYc/cUKAstZVW94/s400/iran3.jpg" alt="" id="BLOGGER_PHOTO_ID_5356458077254559074" border="0"&gt;&lt;/a&gt;&lt;br /&gt;The regression coefficients are then used to scale the target image intensities to those of the reference image:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-NwiSqQlzPY/SlX3IMipItI/AAAAAAAAAYs/LH3E4_hgArc/s1600-h/iran4.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 200px;" src="http://1.bp.blogspot.com/_-NwiSqQlzPY/SlX3IMipItI/AAAAAAAAAYs/LH3E4_hgArc/s400/iran4.jpg" alt="" id="BLOGGER_PHOTO_ID_5356459052004614866" border="0"&gt;&lt;/a&gt;&lt;br /&gt;Click on the above picture to enlarge. On the right hand image the pixels that took part in the normalization are visible as white dots. There's only a few hundred of them, but they suffice. The best part is, the whole procedure is automatic. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-top: 10px; height: 15px;" class="zemanta-pixie"&gt;&lt;a class="zemanta-pixie-a" href="http://reblog.zemanta.com/zemified/4b45f8ed-526a-4862-9d52-f924d37dbcfa/" title="Reblog this post [with Zemanta]"&gt;&lt;img style="border: medium none ; float: right;" class="zemanta-pixie-img" src="http://img.zemanta.com/reblog_e.png?x-id=4b45f8ed-526a-4862-9d52-f924d37dbcfa" alt="Reblog this post [with Zemanta]"&gt;&lt;/a&gt;&lt;span class="zem-script more-related pretty-attribution"&gt;&lt;script type="text/javascript" src="http://static.zemanta.com/readside/loader.js" defer="defer"&gt;&lt;/script&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2952298247043132311?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2952298247043132311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2952298247043132311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2952298247043132311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2952298247043132311'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/07/normalizing-images.html' title='Normalizing Images'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_-NwiSqQlzPY/SlX2nPd5EUI/AAAAAAAAAYk/VQsCIMdBQIM/s72-c/iran1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5200380063914007464</id><published>2009-06-16T12:29:00.000+02:00</published><updated>2009-06-17T09:52:35.780+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='change detection'/><category scheme='http://www.blogger.com/atom/ns#' term='ENVI/IDL'/><category scheme='http://www.blogger.com/atom/ns#' term='MAD'/><title type='text'>Detecting Changes</title><content type='html'>I'll leave the topic of GPU processing in IDL for a while and blog a little on another of my favorite subjects, change detection with multispectral imagery. &lt;a href="http://www2.imm.dtu.dk/~aa/"&gt;Allan Nielsen&lt;/a&gt; and I have collaborated on several papers dealing with his &lt;a href="http://www2.imm.dtu.dk/pubdb/p.php?4695"&gt;MAD&lt;/a&gt; (multivariate alteration detection) algorithm. Here is an unpublished example of MAD in action: &lt;br /&gt;&lt;br /&gt;The disasterous &lt;a href="http://en.wikipedia.org/wiki/2005_Kashmir_earthquake"&gt;earthquake&lt;/a&gt; of October 8, 2005 had a magnitude of 7.6 and was centered near the city of Muzaffarabad in Kashmir. The figure below is an &lt;a href="http://en.wikipedia.org/wiki/ASTER"&gt;ASTER&lt;/a&gt; image over the area acquired on Sept. 7, 2005. The image size is 1000 by 1000 pixels. Ground resolution is 15 meters and the image consists of three bands in the VNIR (visual and near infra-red) spectrum.  Since the terrain is rough, the image was orthorectified using ASTER's stereo capability.&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-NwiSqQlzPY/Sjd-fg0na9I/AAAAAAAAAW8/FPQdYAbQJ_Q/s1600-h/sept_9_05.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_-NwiSqQlzPY/Sjd-fg0na9I/AAAAAAAAAW8/FPQdYAbQJ_Q/s320/sept_9_05.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5347882162377419730" /&gt;&lt;/a&gt;&lt;br /&gt;Here is the same scene acquired with the ASTER satellite on October 27, 2005, similarly orthorectified and carefully registered to the first image:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-NwiSqQlzPY/SjeBVyT2geI/AAAAAAAAAXE/HSmf48Tyr20/s1600-h/oct_27_05.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 320px;" src="http://3.bp.blogspot.com/_-NwiSqQlzPY/SjeBVyT2geI/AAAAAAAAAXE/HSmf48Tyr20/s320/oct_27_05.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5347885293808026082" /&gt;&lt;/a&gt;&lt;br /&gt;We could now look for changes caused by the earthquake just by subtracting the two images from eachother, band-for-band, and this is in fact what many people do. The MAD algorithm is more sophisticated. It generates three new bands (for each of the two images) which are linear combinations of the original bands. Whereas the original bands were sorted by &lt;span style="font-style:italic;"&gt;spectral wavelength&lt;/span&gt;, the new bands of the first image are sorted according to their  &lt;span style="font-style:italic;"&gt;maximum similarity&lt;/span&gt; or &lt;span style="font-style:italic;"&gt;correlation&lt;/span&gt; with the corresponding new bands of the second image. This is called &lt;a href="http://en.wikipedia.org/wiki/Canonical_correlation"&gt;canonical correlation&lt;/a&gt; and is a very old and well-known statistical device. The new bands of the second image are then subtracted from those of the first image to give the &lt;span style="font-style:italic;"&gt;MAD components&lt;/span&gt; (in this case three of them) which contain the change information.&lt;br /&gt;&lt;br /&gt;Forcing the two images in this way to be as similar as possible before subtracting them makes sense. Many differences between the original images will be due to uninteresting effects like solar illumination, differing sensor gains or atmospheric absorption and the canonical correlation procedure will render the MAD components insensitive to such effects.&lt;br /&gt;&lt;br /&gt;However what we really wish to do is to force the &lt;span style="font-style:italic;"&gt;invariant pixels&lt;/span&gt; to be as similar as possible, not the pixels which signal true changes on the ground. This is where the MAD algorithm really excels, because it can be iterated. Having obtained the MAD components, we use them to make an initial estimate of the no-change probability of each pixel in the image. This is possible because of the nice statistical properties of the MAD components. Then we repeat the canonical correlation, this time weighting each pixel with its probability of no change, and recalculate the no-change probabilities. The procedure continues until the similarity measures (the correlations) cease to change. The figure below shows the values of the correlations for each successive iteration. They increase steadily as the invariant pixels become better and better discriminated. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/SjeEUVwp2yI/AAAAAAAAAXM/C3W2594a920/s1600-h/correlations.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/SjeEUVwp2yI/AAAAAAAAAXM/C3W2594a920/s320/correlations.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5347888567499217698" /&gt;&lt;/a&gt;&lt;br /&gt;Have a look at &lt;a href="http://mcanty.homepage.t-online.de/mad1.kml"&gt; the first MAD component&lt;/a&gt; projected onto Google Earth using &lt;a href="http://michaelgalloy.com/"&gt;Mike Galloy&lt;/a&gt;'s IDL program MG_WRITE_KML.PRO. Bright and dark pixels signify change, middle gray no change. Use the slider to vary the overlay's transparency. Tilt the view and get in close to see that, at this resolution, it is the landslides that are most evident.&lt;br /&gt;&lt;br /&gt;Another interesting use of MAD is for automatic radiometric normalization of multitemporal images. Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5200380063914007464?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5200380063914007464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5200380063914007464' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5200380063914007464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5200380063914007464'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/06/detecting-changes.html' title='Detecting Changes'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_-NwiSqQlzPY/Sjd-fg0na9I/AAAAAAAAAW8/FPQdYAbQJ_Q/s72-c/sept_9_05.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-3452615293620820588</id><published>2009-05-31T21:06:00.000+02:00</published><updated>2009-06-02T10:22:58.133+02:00</updated><title type='text'>A neural network on the GPU - Concluded</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_-NwiSqQlzPY/SiTRvDlNvpI/AAAAAAAAAWE/TwfW-XlGCns/s1600-h/crossentropy.jpg"&gt;&lt;img style="float:center; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://4.bp.blogspot.com/_-NwiSqQlzPY/SiTRvDlNvpI/AAAAAAAAAWE/TwfW-XlGCns/s320/crossentropy.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5342625664313704082" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;OK, it's "finished". Here's a summary of the features:&lt;br /&gt;&lt;br /&gt;- Two-layer, feed-forward neural network for supervised  &lt;br /&gt;land cover/land use classification of multispectral imagery, &lt;br /&gt;fully integrated into ENVI.&lt;br /&gt;&lt;br /&gt;- Makes use of the cross entropy cost function and softmax &lt;br /&gt;outputs to model posterior class membership probabilities.&lt;br /&gt;&lt;br /&gt;- Trained with the scaled conjugate gradient algorithm, &lt;br /&gt;guaranteed to converge monotonically to a (local) minimum &lt;br /&gt;in the cost function, see the Figure. The algorithm is &lt;br /&gt;described in detail in Appendix B of my &lt;a href="http://www.crcpress.com/ecommerce_product/product_detail.jsf?catno=7251&amp;isbn=0000000000000&amp;parent_id=&amp;pc="&gt;book&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;- Apart from the number of neurons in the first (hidden) layer, &lt;br /&gt;there are no adjustable parameters whatsoever.&lt;br /&gt;&lt;br /&gt;- Uses the R-operator method (Bishop 1995, &lt;span style="font-style:italic;"&gt;Neural&lt;br /&gt;Networks for Pattern Recognition&lt;/span&gt;) to evaluate the Hessian &lt;br /&gt;(matrix of second derivatives of the cost function) efficiently.&lt;br /&gt;&lt;br /&gt;- All matrix operations are carried out on the GPU using &lt;br /&gt;GPULib IDL bindings to CUDA. CUDA is giving me a speedup &lt;br /&gt;factor of around 3-4 on my system (see earlier blogs) &lt;br /&gt;relative to the CPU version. &lt;br /&gt;&lt;br /&gt;- One heck of a lot faster than ENVI's built-in network &lt;br /&gt;classifier. Also much faster that ENVI's support vector machine, &lt;br /&gt;with comparable accuracy. &lt;br /&gt;&lt;br /&gt;- You can get the code &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-3452615293620820588?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/3452615293620820588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=3452615293620820588' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3452615293620820588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/3452615293620820588'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/05/neural-network-on-gpu-concluded.html' title='A neural network on the GPU - Concluded'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_-NwiSqQlzPY/SiTRvDlNvpI/AAAAAAAAAWE/TwfW-XlGCns/s72-c/crossentropy.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4404572957108063549</id><published>2009-04-29T20:36:00.000+02:00</published><updated>2009-04-29T21:21:57.886+02:00</updated><title type='text'>A neural network on the GPU</title><content type='html'>In my spare time I'm trying to put a neural&lt;br /&gt;network classifier onto the GPU with the help&lt;br /&gt;of &lt;a href="http://www.txcorp.com/products/GPULib/"&gt;GPULib&lt;/a&gt;. It's a lot of work because the training&lt;br /&gt;algorithm, scaled conjugate gradient, is fairly&lt;br /&gt;complicated. I'm converting the original CPU version&lt;br /&gt;that I wrote for my &lt;a href="http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=7251&amp;parent_id=&amp;pc="&gt;book&lt;/a&gt; piece-by-piece, so as&lt;br /&gt;not to get completely frustrated.&lt;br /&gt;&lt;br /&gt;Here is an chunk of code (which is now working)  &lt;br /&gt;which propagates an input vector through the network &lt;br /&gt;to give an output signal that is supposed to reflect &lt;br /&gt;its class membership. (The network is programmed as&lt;br /&gt;an IDL object class, hence all the SELF-references.)&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;Pro GPUFFNCG::forwardPass&lt;br /&gt;   gpuView,*self.N_gpu,self.np,self.LL*self.np,Nv_gpu&lt;br /&gt;   gpuReform,Nv_gpu,self.np,self.LL&lt;br /&gt;;logistic output of hidden layer      &lt;br /&gt;   expnt_gpu = gpuMatrix_Multiply(*self.Gs_gpu,$&lt;br /&gt;               *self.Wh_gpu,/btranspose)&lt;br /&gt;   tmp_gpu = gpuExp(1.0,-1.0,expnt_gpu,0.0,1.0)&lt;br /&gt;   gpuPutArr,fltarr(self.np,self.LL)+1.0,onesL_gpu  &lt;br /&gt;   gpuDiv,onesL_gpu,tmp_gpu,Nv_gpu    &lt;br /&gt;;softmax network output&lt;br /&gt;   Io_gpu = gpuMatrix_Multiply(*self.N_gpu,$&lt;br /&gt;                        *self.Wo_gpu,/btranspose)&lt;br /&gt;   maxIo_gpu = gpuMax(Io_gpu,dimension=2)   &lt;br /&gt;   for k=0,self.KK-1 do begin&lt;br /&gt;      gpuView,Io_gpu,k*self.np,self.np,Iov_gpu&lt;br /&gt;      gpuSub,Iov_gpu,maxIo_gpu,Iov_gpu&lt;br /&gt;   endfor&lt;br /&gt;   A_gpu = gpuExp(Io_gpu)    &lt;br /&gt;   sum_gpu = gpuTotal(A_gpu,2)   &lt;br /&gt;   for k=0,self.KK-1 do begin&lt;br /&gt;      gpuView,*self.M_gpu,k*self.np,self.np,Mv_gpu&lt;br /&gt;      gpuView,A_gpu,k*self.np,self.np,Av_gpu&lt;br /&gt;      gpuDiv,Av_gpu,sum_gpu,Mv_gpu&lt;br /&gt;   endfor&lt;br /&gt;;cleanup&lt;br /&gt;   gpuFree, [tmp_gpu,expnt_gpu,onesL_gpu,Io_gpu,$&lt;br /&gt;             maxIo_gpu,A_gpu,sum_gpu]&lt;br /&gt;End&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;I try to make as much use of GPUVIEW as possible &lt;br /&gt;in order to avoid shifting arrays around, and of &lt;br /&gt;course I try to avoid transfers back to the CPU. &lt;br /&gt;&lt;br /&gt;The hardest part is calculating the matrix of second&lt;br /&gt;derivatives of the cost function with respect to the&lt;br /&gt;synaptic weights (the Hessian). This is where I hope&lt;br /&gt;for a big speedup using GPULib. Still working on that.&lt;br /&gt;&lt;br /&gt;More to come, I hope.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4404572957108063549?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4404572957108063549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4404572957108063549' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4404572957108063549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4404572957108063549'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/04/neural-network-on-gpu.html' title='A neural network on the GPU'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1820080966541784092</id><published>2009-02-24T21:03:00.000+01:00</published><updated>2009-02-24T21:31:13.314+01:00</updated><title type='text'>Home page update</title><content type='html'>Just re-vamped my &lt;a href="http://mcanty.homepage.t-online.de/"&gt;home page&lt;/a&gt; a bit, making terribly efficient use of all 10 (count them, ten) MB of webspace that Deutsche Telekom generously provides me free of charge with my call and surf "comfort package", as they humorously call it.&lt;br /&gt;&lt;br /&gt;I've put up the most recent versions of my ENVI extensions, some of which actually work. The kernel PCA and kernel k-means routines, both of which use GPULib, seem stable enough. I'm still stuggling with a CUDA version of the Gaussian mixture classifier, unsure as usual if there's a bug in GPULib or if I'm to blame.&lt;br /&gt;&lt;br /&gt;All of the programs and their subroutines have been churned through IDLDOC, but the list of warnings about partial or missing documentation is still very long. I want to fill in the gaps gradually as I have time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1820080966541784092?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1820080966541784092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1820080966541784092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1820080966541784092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1820080966541784092'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/02/home-page-update.html' title='Home page update'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4482134845727384346</id><published>2009-01-19T18:06:00.000+01:00</published><updated>2009-01-19T18:25:48.453+01:00</updated><title type='text'>Learning IDLDOC</title><content type='html'>I just installed Mike Galloy's IDLDOC plugin for the IDL Workbench and I'm learning how to use it. My first feeble attempts can be scoffed at on my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;software page&lt;/a&gt;, where I've started to document the latest versions of the ENVI extensions in my &lt;a href="http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=7251&amp;parent_id=&amp;pc="&gt;book&lt;/a&gt;. By the time the Revised Second Edition comes out later this year, I hope to have mastered the techniques. Already, though, I think it's a fantastic tool.&lt;br /&gt;&lt;br /&gt;One problem I have is that the ".\" prefixed to the filenames in ALL-FILES.HTML generated by IDLDOC is confusing my (much-loved) Firefox browser. IE6 has no difficulty. Just now I'm removing them by hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4482134845727384346?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4482134845727384346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4482134845727384346' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4482134845727384346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4482134845727384346'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2009/01/learning-idldoc.html' title='Learning IDLDOC'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2615755638467511103</id><published>2008-12-29T11:24:00.000+01:00</published><updated>2008-12-29T22:09:50.674+01:00</updated><title type='text'>Using GPULib 1.0</title><content type='html'>Now that I've got GPULib 1.0 up and running, I'm making use&lt;br /&gt;of the new functional forms of the library routines to write&lt;br /&gt;kernel-based algorithms. Given a set of m n-dimensional&lt;br /&gt;observation vectors G_i&lt;br /&gt;&lt;br /&gt;G_i = ( g_1, g_2 ... g_n )_i,           i=1,2,...m,&lt;br /&gt;&lt;br /&gt;in the form of a m x n data matrix:&lt;br /&gt;&lt;br /&gt;G = ( G_1, G_2, ... G_m)^T&lt;br /&gt;&lt;br /&gt;where ^T denotes transposition, I want to calculate the symmetric&lt;br /&gt;m x m Gaussian kernel matrix&lt;br /&gt;&lt;br /&gt;K_ij = exp(-gma ||G_i-G_j||^2 ),    i,j = 1,2,...m.&lt;br /&gt;&lt;br /&gt;In ordinary IDL:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;function kernel_matrix, G, gma=gma&lt;br /&gt;  m = n_elements(G[0,*])&lt;br /&gt;  K = transpose(total(G^2,1))##(intarr(m)+1)&lt;br /&gt;  K = K + transpose(K) - 2*G##transpose(G)&lt;br /&gt;  return, exp(-gma*K)&lt;br /&gt;end  &lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;&lt;br /&gt;WIth GPULib:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;function gpukernel_matrix, G_gpu, gma=gma&lt;br /&gt;  m = G_gpu.dimensions[1]&lt;br /&gt;  n = G_gpu.dimensions[0]  &lt;br /&gt;  onesm_gpu = gpuputarr(fltarr(m)+1)&lt;br /&gt;  onesn_gpu = gpuputarr(fltarr(1,n)+1)  &lt;br /&gt;  G2_gpu = gpumult(G_gpu,G_gpu)     &lt;br /&gt;  tmp1_gpu = gpumatrix_multiply(onesn_gpu,G2_gpu)&lt;br /&gt;  gpufree,G2_gpu &lt;br /&gt;  K_gpu = gpumatrix_multiply(onesm_gpu,tmp1_gpu) &lt;br /&gt;  tmp2_gpu = gpumatrix_multiply(tmp1_gpu, $&lt;br /&gt;              onesm_gpu,/atranspose,/btranspose)&lt;br /&gt;  gpufree,[onesm_gpu,onesn_gpu,tmp1_gpu]&lt;br /&gt;  K_gpu = gpuadd(K_gpu,tmp2_gpu,lhs=K_gpu)&lt;br /&gt;  gpufree,tmp2_gpu&lt;br /&gt;  tmp3_gpu = gpumatrix_multiply(G_gpu,G_gpu, $&lt;br /&gt;                  /atranspose)&lt;br /&gt;  K_gpu = gpuadd(1.0,K_gpu,-2.0,tmp3_gpu,0.0,$&lt;br /&gt;                 lhs=K_gpu)&lt;br /&gt;  gpufree,tmp3_gpu &lt;br /&gt;  K_gpu = gpuexp(1.0,-gma,K_gpu,0.0,0.0,$&lt;br /&gt;                 lhs=K_gpu)&lt;br /&gt;  return, K_gpu&lt;br /&gt;end  &lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Since this routine is called many times in the&lt;br /&gt;processing of a multispectral image, a memory&lt;br /&gt;leak would be disasterous. Note the careful use&lt;br /&gt;of the LHS keyword and of the GPUFREE procedure&lt;br /&gt;to avoid this.&lt;br /&gt;&lt;br /&gt;The code is now more readable than for the&lt;br /&gt;procedural forms, but would be more readable&lt;br /&gt;still if there were a GPUTRANSPOSE() function and&lt;br /&gt;if GPUTOTAL() had a dimension parameter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2615755638467511103?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2615755638467511103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2615755638467511103' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2615755638467511103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2615755638467511103'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/12/using-gpulib-10.html' title='Using GPULib 1.0'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-2790737771468484475</id><published>2008-12-12T17:16:00.000+01:00</published><updated>2008-12-12T18:11:35.137+01:00</updated><title type='text'>More on GPULib and Kernel Methods</title><content type='html'>My last post was way back in August and I'm writing this one really just to keep the blog alive, so to speak. So far I haven't been able to get GPULib 1.0 going on my system, so I have no idea if the programs on my website will still run under 1.0. I'm getting help from Tech-X and haven't given up hope.&lt;br /&gt;&lt;br /&gt;Anyway, I'm very keen to apply CUDA (via GPULib) to the kind of nonlinear algorithms (kernel methods) I've described previously. These algorithms involve two steps: training and generalization. In remote sensing applications, the training data set can be kept fairly small (order 10^3), but generalization invariably means processing all the pixel vectors in an image, typically of the order 10^6-10^8. Since generalization can be safely done in single precision and involves manipulation of large matrices, it is a natural task for the GPU.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-2790737771468484475?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/2790737771468484475/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=2790737771468484475' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2790737771468484475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/2790737771468484475'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/12/more-on-gpulib-and-kernel-methods.html' title='More on GPULib and Kernel Methods'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4061811172151990633</id><published>2008-08-28T18:58:00.000+02:00</published><updated>2008-08-28T20:58:52.767+02:00</updated><title type='text'>GPULib: Kernel K-means</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mcanty.homepage.t-online.de/kkmeans.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 320px;" src="http://mcanty.homepage.t-online.de/kkmeans.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Just like principal components analysis, the subject of previous posts, the well-known K-means clustering algorithm can be kernelized. Shawe-Taylor and Cristianini, in their excellent book &lt;a href="http://www.kernel-methods.net/"&gt;Kernel Methods for Pattern Analysis&lt;/a&gt;, give Matlab code. Based on that I've written an &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;ENVI extension&lt;/a&gt; for kernel K-means using the GPULib library. Here are some times for the multispectral image clustering shown above:&lt;br /&gt;&lt;br /&gt;Without GPULib&lt;span style="font-family:monospace;"&gt;&lt;/span&gt;&lt;listing&gt;&lt;br /&gt;-------------------&lt;br /&gt;Kernel K-means&lt;br /&gt;Thu Aug 28 19:44:12 2008&lt;br /&gt;-------------------&lt;br /&gt;input file juelich_august_29_pca&lt;br /&gt;Number of Classes       8&lt;br /&gt;elapsed time:        32.500000&lt;br /&gt;Result written to memory&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;With GPULib&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;-------------------&lt;br /&gt;Kernel K-means&lt;br /&gt;Thu Aug 28 19:48:16 2008&lt;br /&gt;-------------------&lt;br /&gt;input file juelich_august_29_pca&lt;br /&gt;Number of Classes       8&lt;br /&gt;elapsed time:        4.0160000&lt;br /&gt;Result written to memory&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Speedup factor:  8 .&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4061811172151990633?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4061811172151990633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4061811172151990633' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4061811172151990633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4061811172151990633'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/08/gpulib-kernel-k-means.html' title='GPULib: Kernel K-means'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-1959093540716488067</id><published>2008-08-10T12:35:00.000+02:00</published><updated>2008-08-14T14:20:46.252+02:00</updated><title type='text'>Kernelized PCA with GPULib</title><content type='html'>I haven't had much time lately to play with GPULib, although there are plenty of ENVI/IDL apps that I want to experiment with. An ENVI extension for kernelized PCA (nonlinear principal  components analysis using a radial basis kernel, mentioned in an earlier blog) can be downloaded from my &lt;a href="http://mcanty.homepage.t-online.de/software.html"&gt;software website&lt;/a&gt;. It is pretty rudimentary, but illustrates the use of quite a few GPULib functions. See also this short &lt;a href="http://mcanty.homepage.t-online.de/nonlinear1.ppt"&gt;ppt&lt;/a&gt; on nonlinear PCA with GPULib.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-1959093540716488067?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/1959093540716488067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=1959093540716488067' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1959093540716488067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/1959093540716488067'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/08/kernelized-pca-with-gpulib.html' title='Kernelized PCA with GPULib'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4184223862153124653</id><published>2008-07-19T13:35:00.000+02:00</published><updated>2008-07-20T12:32:47.746+02:00</updated><title type='text'>GPULIB (Clustering speedup)</title><content type='html'>The clustering program that I've been re-working with GPULib fits a mixture of K multivariate Gaussians to a cloud of n N-dimensional image pixel vectors using the Expectation Maximization algorithm:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;  1. Initialize n x K cluster membership&lt;br /&gt;     probability matrix U&lt;br /&gt;  &lt;br /&gt;  2. Maximization step: calculate &lt;br /&gt;     K N-component mean vectors,&lt;br /&gt;     K NxN covariance matrices,  &lt;br /&gt;     K mixture coefficients&lt;br /&gt;&lt;br /&gt;  3. Expectation step: recalcuate  &lt;br /&gt;     U and normalize.&lt;br /&gt;&lt;br /&gt;  4. If U hasn't changed significantly, &lt;br /&gt;     stop, else go to 2.&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Step 3 requires inversion of the cluster covariance matrices, which has to be done on the CPU. Apart from that, I still get into trouble every time I use &lt;tt&gt;gpuWhere&lt;/tt&gt; (don't know why yet). So, since I have to check U for zeroes, I have to  do its normalization on the CPU as well. Here is my attempt at normalization on the GPU, which usually crashes IDL:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;; normalize U&lt;br /&gt;  gpuMatrix_Multiply,U_gpu,onesKxK_gpu,den_gpu&lt;br /&gt;  gpuEq,den_gpu,fltarr(n,K),zeroes_gpu&lt;br /&gt;  gpuWhere,zeroes_gpu,ind_gpu,count&lt;br /&gt;  if count gt 0 then $&lt;br /&gt;     gpuSubscript,ind_gpu,den_gpu,$&lt;br /&gt;            fltarr(count)+1,/LHS&lt;br /&gt;  gpuDiv,U_gpu,den_gpu,U_gpu   &lt;br /&gt;  gpuFree,[den_gpu,ind_gpu,zeroes_gpu]&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Speedup (so far) on a 1000 x 1000 x 4 image clustered into 8 classes (i.e. n=1,000,000, N=4, K=8) is a factor of 2.8.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4184223862153124653?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4184223862153124653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4184223862153124653' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4184223862153124653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4184223862153124653'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/07/gpulib-clustering-speedup.html' title='GPULIB (Clustering speedup)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-4280251811595964093</id><published>2008-07-10T16:51:00.000+02:00</published><updated>2008-07-10T17:56:37.513+02:00</updated><title type='text'>GPULIB (trip to never-neverland)</title><content type='html'>I seem to be making a little progress with the clustering algorithm. One problem is that I have to invert a covariance matrix on each iteration, which means jumping back to the CPU to do it.  This probably slows things down. Doesn't CUBLAS provide matrix inversion?&lt;br /&gt;&lt;br /&gt;Anyway, while coding I stumbled into a horrible trap:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;gpuPutArr,A,A_gpu&lt;br /&gt;gpuView,A_gpu,n,m,Av_gpu&lt;br /&gt;;...&lt;br /&gt;;lots of code, so I kind of forgot&lt;br /&gt;;what Av_gpu is&lt;br /&gt;;...&lt;br /&gt;gpuFree,A_gpu&lt;br /&gt;gpuFree,Av_gpu ; very, very bad thing to do&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;This put me Somewhere Over the Rainbow. Nothing, but nothing works anymore on the GPU until I exit IDL and restart. Took a long time to get back to earth. Incidently, another way to get up there with Judy Garland is to forget that&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;gpuWhere,A_gpu,ind_gpu,count&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;overwrites &lt;tt&gt;A_gpu&lt;/tt&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-4280251811595964093?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/4280251811595964093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=4280251811595964093' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4280251811595964093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/4280251811595964093'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/07/gpulib-trip-to-never-neverland.html' title='GPULIB (trip to never-neverland)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5857951323353719445</id><published>2008-07-08T16:42:00.000+02:00</published><updated>2008-07-08T16:58:36.067+02:00</updated><title type='text'>GPULIB succes story (cont'd)</title><content type='html'>Regarding my last post, Peter Messmer at Tech-X pointed out to me that I should use &lt;tt&gt;gpuView&lt;/tt&gt; rather than shifting subarrays around with &lt;tt&gt;gpuSubArr&lt;/tt&gt;. Doesn't make things much faster in this case, but the code is nicer:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;while i lt num_rows do begin&lt;br /&gt;; ggi = GG[*,i*num_cols:(i+1)*num_cols-1]&lt;br /&gt;  gpuView,GG_gpu,i*num_cols*num_bands,$&lt;br /&gt;    num_bands*num_cols,ggi_gpu&lt;br /&gt;  gpuReform,ggi_gpu,num_bands,num_cols&lt;br /&gt;; KK = transpose(G2)##(intarr(num_cols)+1)&lt;br /&gt;  gpuMatrix_Multiply,onesnc_gpu,G2_gpu,$&lt;br /&gt;     KK_gpu,/btranspose&lt;br /&gt;; KK = KK + transpose(intarr(m)+1)##$&lt;br /&gt;;       GG2[i*num_cols:(i+1)*num_cols-1]&lt;br /&gt;  gpuView,GG2_gpu,i*num_cols,num_cols,gg2i_gpu&lt;br /&gt;  gpuMatrix_Multiply,gg2i_gpu,$&lt;br /&gt;        onesm_gpu,res_gpu,/btranspose&lt;br /&gt;  gpuAdd,KK_gpu,res_gpu,KK_gpu&lt;br /&gt;; KK = KK - 2*G##transpose(ggi)&lt;br /&gt;  gpuMatrix_Multiply,ggi_gpu,G_gpu,$&lt;br /&gt;        res_gpu,/atranspose&lt;br /&gt;  gpuAdd,1.0,KK_gpu,-2.0,res_gpu,0.0,KK_gpu&lt;br /&gt;; KK = exp(-gma*KK)&lt;br /&gt;  gpuExp,1.0,-gma,KK_gpu,0.0,0.0,KK_gpu &lt;br /&gt;; image[*,i,*] = alpha[*,0:npc-1]##KK&lt;br /&gt;  gpuMatrix_Multiply,KK_gpu,alpha_gpu,res1_gpu&lt;br /&gt;  gpuGetArr,res1_gpu,res&lt;br /&gt;  image[*,i,*]=res&lt;br /&gt;  i++ ; next row&lt;br /&gt;endwhile&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;With my new-found skills ;-) I want next to have a go at image clustering. If you  cluster &lt;span style="font-weight: bold;"&gt;all &lt;/span&gt;&lt;span&gt;of the pixel vectors in a multispectral image (typically millions) then things can get pretty slow.&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5857951323353719445?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5857951323353719445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5857951323353719445' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5857951323353719445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5857951323353719445'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/07/gpulib-succes-story-contd.html' title='GPULIB succes story (cont&apos;d)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6578499059047576824</id><published>2008-07-01T15:56:00.000+02:00</published><updated>2008-07-01T17:19:03.660+02:00</updated><title type='text'>GPULIB success story (at last)</title><content type='html'>In kernelized PCA (nonlinear principal components analysis) you have to diagonalize a large (say 1000x1000) Gram matrix (real, symmetric kernel matrix) and then project the original image along the most significant eigenvectors. There is no way to do diagonalizations on the GPU (yet?), but the projection part is also very time consuming. Here is my solution using GPULIB. The commented statements are the original code on the CPU:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;start_time=systime(2)&lt;br /&gt;while i lt num_rows do begin&lt;br /&gt;; ggi = GG[*,i*num_cols:(i+1)*num_cols-1]  &lt;br /&gt;   gpuSubArr,GG_gpu,[0L,-1L],$&lt;br /&gt;         [i*num_cols,(i+1)*num_cols-1],$&lt;br /&gt;         ggi_gpu,[0L,-1L],[0L,-1L]  &lt;br /&gt;; KK = transpose(G2)##(intarr(num_cols)+1)&lt;br /&gt;   gpuMatrix_Multiply,onesnc_gpu,$&lt;br /&gt;          G2_gpu,KK_gpu,/btranspose&lt;br /&gt;; KK = KK + transpose(intarr(m)+1)##$&lt;br /&gt;;        GG2[i*num_cols:(i+1)*num_cols-1]&lt;br /&gt;   gpuSubArr,GG2_gpu,[i*num_cols,(i+1)*num_cols-1],$&lt;br /&gt;          gg2i_gpu,[0L,-1L]&lt;br /&gt;   gpuMatrix_Multiply,gg2i_gpu,onesm_gpu,res_gpu,$&lt;br /&gt;         /btranspose&lt;br /&gt;   gpuAdd,KK_gpu,res_gpu,KK_gpu &lt;br /&gt;; KK = KK - 2*G##transpose(ggi)&lt;br /&gt;   gpuMatrix_Multiply,ggi_gpu,G_gpu,res_gpu,$&lt;br /&gt;         /atranspose&lt;br /&gt;   gpuAdd,1.0,KK_gpu,-2.0,res_gpu,0.0,KK_gpu&lt;br /&gt;; KK = exp(-gma*KK)&lt;br /&gt;   gpuExp,1.0,-gma,KK_gpu,0.0,0.0,KK_gpu  &lt;br /&gt;; image[*,i,*] = alpha[*,0:npc-1]##KK&lt;br /&gt;   gpuMatrix_Multiply,KK_gpu,alpha_gpu,res1_gpu&lt;br /&gt;   gpuGetArr,res1_gpu,res &lt;br /&gt;   image[*,i,*]=res&lt;br /&gt;   i++ &lt;br /&gt;endwhile&lt;br /&gt;print,'elapsed time: ',systime(2)-start_time&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Here is the time taken to project a 1000x1000x6 image on the GPU&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;elapsed time:        12.093000&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;and on the CPU&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;elapsed time:        197.39100&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;for a speedup of 16. Not bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6578499059047576824?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6578499059047576824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6578499059047576824' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6578499059047576824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6578499059047576824'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/07/gpulib-success-story-at-last.html' title='GPULIB success story (at last)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-6547076039133570927</id><published>2008-06-30T15:57:00.000+02:00</published><updated>2008-06-30T16:44:48.608+02:00</updated><title type='text'>GPULIB Where-Woes</title><content type='html'>No one can live without the where() function. Is it faster on the GPU? Here is my attempt to time it:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;pro test_gpuWhere&lt;br /&gt; gpuinit&lt;br /&gt; A = float(round(randomu(s,1000000)))&lt;br /&gt; st = systime(2)&lt;br /&gt; for i=0,99 do begin&lt;br /&gt;    gpuPutArr, A, A_gpu&lt;br /&gt;    ind = where(A,count)&lt;br /&gt; endfor &lt;br /&gt; print,count&lt;br /&gt; CPUtime = systime(2)-st&lt;br /&gt; st = systime(2)&lt;br /&gt; _ = temporary(count)&lt;br /&gt; for i=0,99 do begin&lt;br /&gt;    gpuPutArr, A, A_gpu&lt;br /&gt;    gpuWhere,A_gpu,ind_gpu,count&lt;br /&gt; endfor &lt;br /&gt; print,count&lt;br /&gt; GPUtime = systime(2)-st&lt;br /&gt; print, 'Speedup: ',CPUtime/GPUtime&lt;br /&gt; gpuFree,A_gpu&lt;br /&gt; gpuFree,ind_gpu&lt;br /&gt;end&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Several things to notice here:&lt;br /&gt;&lt;br /&gt;A is an array of "float flags", i.e. &lt;span style="font-family:courier new;"&gt;1.0s&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;0.0s&lt;/span&gt;. Everything, repeat, everything in GPULIB is float. Also the &lt;span style="font-family:courier new;"&gt;count&lt;/span&gt; parameter in &lt;span style="font-family:courier new;"&gt;gpuWhere&lt;/span&gt; returns the sum of A, not the number of nonzero elements.&lt;br /&gt;&lt;br /&gt;Before reusing &lt;span style="font-family:courier new;"&gt;count&lt;/span&gt;  in &lt;span style="font-family:courier new;"&gt;gpuWhere&lt;/span&gt; I have to undefine it, because it is returned as  longint from &lt;span style="font-family:courier new;"&gt;where()&lt;/span&gt;. GPULIB will report an error otherwise.&lt;br /&gt;&lt;br /&gt;In the loop, I have to use&lt;span style="font-family:courier new;"&gt; putArr,A,A_gpu&lt;/span&gt;  each time, because&lt;span style="font-family:courier new;"&gt; gpuWhere &lt;/span&gt;overwrites its first argument. &lt;br /&gt;&lt;br /&gt;So is all this worth the trouble?&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;% Compiled module: TEST_GPUWHERE.&lt;br /&gt;     500933&lt;br /&gt;     500933.&lt;br /&gt;Speedup:        1.3521798&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;Evidently not.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-6547076039133570927?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/6547076039133570927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=6547076039133570927' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6547076039133570927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/6547076039133570927'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/06/gpulib-where-woes.html' title='GPULIB Where-Woes'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7943996320160393990</id><published>2008-06-30T15:32:00.000+02:00</published><updated>2008-06-30T15:52:19.022+02:00</updated><title type='text'>GPULIB rude awakening</title><content type='html'>Turns out I had set the GPU into some kind of undefined state by incorrectly using the gpuWhere procedure in the session in the last post. Here's the correct output from test_gpuMatrix_Multiply:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;Length  :         1000&lt;br /&gt;CPU time:      0.047000170&lt;br /&gt;GPU time:      0.030999899&lt;br /&gt;Speedup :        1.5161395&lt;br /&gt;Length  :        10000&lt;br /&gt;CPU time:       0.34399986&lt;br /&gt;GPU time:       0.26500010&lt;br /&gt;Speedup :        1.2981122&lt;br /&gt;Length  :       100000&lt;br /&gt;CPU time:        3.0780001&lt;br /&gt;GPU time:        2.6250000&lt;br /&gt;Speedup :        1.1725715&lt;br /&gt;Length  :      1000000&lt;br /&gt;CPU time:        30.734000&lt;br /&gt;GPU time:        26.282000&lt;br /&gt;Speedup :        1.1693935&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;It was way too good to be true. Ah well.&lt;br /&gt;&lt;br /&gt;OK, what about simple array products?&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;pro test_gpuMult&lt;br /&gt;; initialize&lt;br /&gt;   gpuinit&lt;br /&gt;; array of 1000000 3-element observation vectors (rows)   &lt;br /&gt;   A = randomu(s,3,1000000)&lt;br /&gt;   gpuPutArr,A,A_gpu&lt;br /&gt;; calculate square of A on the CPU            &lt;br /&gt;   start = systime(2)&lt;br /&gt;   for j=0L,99 do C = A*A&lt;br /&gt;   CPUtime = systime(2)-start&lt;br /&gt;   print, 'CPU time: ',CPUtime &lt;br /&gt;; now on the GPU      &lt;br /&gt;   start = systime(2)&lt;br /&gt;   for j=0L,99 do gpuMult,A_gpu,A_gpu,C_gpu&lt;br /&gt;   GPUtime = systime(2)-start&lt;br /&gt;   print,'GPU time: ', GPUtime&lt;br /&gt;   print,'Speedup : ', CPUtime/GPUtime&lt;br /&gt;   gpuFree,A_gpu    &lt;br /&gt;; check that the results are the same   &lt;br /&gt;   gpuGetArr,C_gpu,C1&lt;br /&gt;   print,'Check:'&lt;br /&gt;   print, total(C-C1)&lt;br /&gt;   gpuFree,C_gpu&lt;br /&gt;end&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;This gives a speedup of 10:&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;% Compiled module: TEST_GPUMULT.&lt;br /&gt;CPU time:        2.1100001&lt;br /&gt;GPU time:       0.20300007&lt;br /&gt;Speedup :        10.394086&lt;br /&gt;Check:&lt;br /&gt;     0.000000&lt;br /&gt;&lt;/listing&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7943996320160393990?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7943996320160393990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7943996320160393990' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7943996320160393990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7943996320160393990'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/06/gpulib-sorrows.html' title='GPULIB rude awakening'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-7946793642593110133</id><published>2008-06-28T17:30:00.000+02:00</published><updated>2008-06-29T12:28:40.772+02:00</updated><title type='text'>GPULIB continued</title><content type='html'>It is often necessary to calculate the covariance matrix of a multispectral image from the array of observation vectors (pixels). The program&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;pro test_gpuMatrix_Multiply&lt;br /&gt;; initialize&lt;br /&gt; gpuinit&lt;br /&gt; for i=3,6 do begin&lt;br /&gt;; array of 6-element observation vectors (rows) &lt;br /&gt;    A = randomu(s,6,10L^i)&lt;br /&gt;    gpuPutArr,A,A_gpu&lt;br /&gt;; calculate covariance matrix C on the CPU          &lt;br /&gt;    start = systime(2)&lt;br /&gt;    for j=0L,99 do C = $&lt;br /&gt;      Matrix_Multiply(A,A,/btranspose)&lt;br /&gt;    CPUtime = systime(2)-start&lt;br /&gt;    print, 'Length  : ',10L^i&lt;br /&gt;    print, 'CPU time: ',CPUtime&lt;br /&gt;; now on the GPU    &lt;br /&gt;    start = systime(2)&lt;br /&gt;    for j=0L,999 do $&lt;br /&gt;     gpuMatrix_Multiply,A_gpu,A_gpu,C_gpu,/btranspose&lt;br /&gt;    GPUtime = systime(2)-start&lt;br /&gt;    print,'GPU time: ', GPUtime/10&lt;br /&gt;    print,'Speedup : ', 10*CPUtime/GPUtime&lt;br /&gt;    gpuFree,A_gpu  &lt;br /&gt; endfor&lt;br /&gt;; check that the results are the same &lt;br /&gt; gpuGetArr,C_gpu,C1&lt;br /&gt; print,'Check:'&lt;br /&gt; print, determ(C),determ(C1) &lt;br /&gt; gpuFree,C_gpu&lt;br /&gt;end&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;produces&lt;br /&gt;&lt;listing&gt;&lt;br /&gt;% Compiled module: TEST_GPUMATRIX_MULTIPLY.&lt;br /&gt;Length  :         1000&lt;br /&gt;CPU time:      0.030999899&lt;br /&gt;GPU time:     0.0062000036&lt;br /&gt;Speedup :        4.9999808&lt;br /&gt;Length  :        10000&lt;br /&gt;CPU time:       0.25000000&lt;br /&gt;GPU time:     0.0046999931&lt;br /&gt;Speedup :        53.191567&lt;br /&gt;Length  :       100000&lt;br /&gt;CPU time:       0.96900010&lt;br /&gt;GPU time:     0.0046999931&lt;br /&gt;Speedup :        206.17054&lt;br /&gt;Length  :      1000000&lt;br /&gt;CPU time:        9.5620000&lt;br /&gt;GPU time:     0.0032000065&lt;br /&gt;Speedup :        2988.1190&lt;br /&gt;Check:&lt;br /&gt;6.33251e+030 6.33251e+030&lt;br /&gt;&lt;/listing&gt;&lt;br /&gt;So for instance the covariance matrix of a 1000x1000x6  multispectral image could be calculated 3000 times faster on the GPU!? My ENVI/IDL change detection extension iterates on the weighted covariance matrix of a bitemporal image. Sooo ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-7946793642593110133?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/7946793642593110133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=7946793642593110133' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7946793642593110133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/7946793642593110133'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/06/gpulib-continued_8892.html' title='GPULIB continued'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1477876132593701158.post-5551906723916563019</id><published>2008-06-26T19:16:00.000+02:00</published><updated>2008-06-26T20:01:02.983+02:00</updated><title type='text'>GPULIB Trials (and Tribulations)</title><content type='html'>With active and friendly help from &lt;a href="http://www.txcorp.com/"&gt;Tech-X&lt;/a&gt; Corporation I've been experimenting with &lt;a href="http://www.txcorp.com/technologies/GPULib/index.php"&gt;&lt;span style="text-decoration: underline;"&gt;GPULIB&lt;/span&gt;&lt;/a&gt;, an IDL/Matlab/Python ...  interface to NVIDIA's &lt;a href="http://www.nvidia.com/object/cuda_home.html#"&gt;CUDA.&lt;/a&gt; I was (and still am) hoping to speed up some of the ENVI/IDL extensions for remote sensing image analysis described in my &lt;a href="http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=7251&amp;amp;parent_id=&amp;amp;pc="&gt;book&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So I've started blogging (at my age!) to document my experiences for anyone who wants to take a similar plunge into the world of "massively parallel computing" on his or her home PC.&lt;br /&gt;&lt;br /&gt;Mine is:&lt;br /&gt;&lt;br /&gt;Intel Pentium 4 650, 3400 MHz (17 x 200)&lt;br /&gt;Intel Lakeport-G i945G&lt;br /&gt;2048 MB  (DDR2 SDRAM)&lt;br /&gt;NVIDIA GeForce 8600 GT  (512 MB)&lt;br /&gt;&lt;br /&gt;Having gotten a Windows XP build from Tech-X (I evidently don't have the right C++ compiler to do my own builds), I did the following&lt;br /&gt;&lt;br /&gt;-Copied GPULIB.DLM and GPULIB.DLL to C:\Programme\ITT\IDL70\bin\bin.x86  (my DLL/DLM Path)&lt;br /&gt;&lt;br /&gt;-Placed GPUINIT.PRO in my IDL path&lt;br /&gt;&lt;br /&gt;-Downloaded and installed the latest NVIDIA drivers for my graphics card&lt;br /&gt;&lt;br /&gt;-Downloaded the CUDA Toolkit version 1.1 for Windows XP 32bit&lt;br /&gt;&lt;br /&gt;-Copied the DLLs (including CUDART.DLL) to  C:\Programme\ITT\IDL70\bin\bin.x86&lt;br /&gt;&lt;br /&gt;-Started IDL 7.0 (with ENVI 4.5)&lt;br /&gt;&lt;br /&gt;-Ran the benchmark program BENCH.PRO&lt;br /&gt;&lt;br /&gt;Here's what it said:&lt;br /&gt;&lt;br /&gt;% Compiled module: BENCH.&lt;br /&gt;   0.756607      2.33993     0.196372     0.516154    0.0442747     0.839950&lt;br /&gt;   0.756607      2.33993     0.196372     0.516154    0.0442747     0.839950&lt;br /&gt;N iter   =       50&lt;br /&gt;CPU Time =        9.2029998&lt;br /&gt;GPU Time =       0.10900021&lt;br /&gt;Speedup  =  &lt;span style="font-weight: bold;"&gt;       84.431032 &lt;/span&gt;(emphasis added)&lt;br /&gt;&lt;br /&gt;Wow! If that isn't exciting I don't know what is. This with a cheap, passively-cooled graphics card!&lt;br /&gt;&lt;br /&gt;Of course, what BENCH.PRO actually does is calculate the log gamma function of a 1000000-element array 50 times, something I seldom have occasion to do in my ENVI extensions.&lt;br /&gt;&lt;br /&gt;And that's where the story starts. Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1477876132593701158-5551906723916563019?l=fwenvi-idl.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fwenvi-idl.blogspot.com/feeds/5551906723916563019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1477876132593701158&amp;postID=5551906723916563019' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5551906723916563019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1477876132593701158/posts/default/5551906723916563019'/><link rel='alternate' type='text/html' href='http://fwenvi-idl.blogspot.com/2008/06/gpulib-trials-and-tribulations.html' title='GPULIB Trials (and Tribulations)'/><author><name>Mort Canty</name><uri>http://www.blogger.com/profile/00439088437367887072</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://3.bp.blogspot.com/-fl4Gv2dHneo/Tg9vH4C-rVI/AAAAAAAAAu0/Nb8A5KKuWUE/s220/P7040047.JPG'/></author><thr:total>2</thr:total></entry></feed>
