<?xml version='1.0' encoding='utf-8' ?>
<!--  If you are running a bot please visit this policy page outlining rules you must respect. http://www.livejournal.com/bots/  -->
<rss version='2.0' xmlns:lj='http://www.livejournal.org/rss/lj/1.0/' xmlns:media='http://search.yahoo.com/mrss/' xmlns:atom10='http://www.w3.org/2005/Atom'>
<channel>
  <title>This is Not a Brain Surgery</title>
  <link>http://notbrainsurgery.livejournal.com/</link>
  <description>This is Not a Brain Surgery - LiveJournal.com</description>
  <lastBuildDate>Fri, 24 May 2013 05:30:12 GMT</lastBuildDate>
  <generator>LiveJournal / LiveJournal.com</generator>
  <lj:journal>notbrainsurgery</lj:journal>
  <lj:journalid>1461624</lj:journalid>
  <lj:journaltype>personal</lj:journaltype>
  <image>
    <url>http://l-userpic.livejournal.com/21198071/1461624</url>
    <title>This is Not a Brain Surgery</title>
    <link>http://notbrainsurgery.livejournal.com/</link>
    <width>100</width>
    <height>78</height>
  </image>

<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/42157.html</guid>
  <pubDate>Fri, 24 May 2013 05:30:12 GMT</pubDate>
  <title>temperature measurement</title>
  <link>http://notbrainsurgery.livejournal.com/42157.html</link>
  <description>As part of a small research project I have set to built a small sensor network to monitor temperatures at my home and office. Doing this I did some hardware (wireless Zigbee temperature sensors) and software to collect data from existing ones. The purpose of this post is to share quick links to scripts I have developed.&lt;br /&gt;&lt;br /&gt;1. Main project page: &lt;a href=&quot;https://github.com/vzaliva/lmsensors-cosm&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;https://github.com/vzaliva/xbee_temp_sensor&lt;/a&gt; contains Wireless sensor harware design and a collection scripts for:&lt;br /&gt;  a) Collecting data from my XBee-based sensors&lt;br /&gt;  b) Collecting data from &lt;a href=&quot;https://www.amazon.com/dp/B004YZFU1Q/ref=as_li_ss_til?tag=wwwcrocodilorg&amp;amp;camp=0&amp;amp;creative=0&amp;amp;linkCode=as4&amp;amp;creativeASIN=B004YZFU1Q&amp;amp;adid=0P5WM5XVKJE5WYZ40Q4H&amp;amp;&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;3M CT-50 Radio thermostat&lt;/a&gt;&lt;br /&gt;  c) Recording data from Weatherunderground.com weather stations&lt;br /&gt;  d) Submitting data to COSM (now renamed to&lt;a href=&quot;http://XIVELY.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt; XIVELY.com&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;2. Data collection from a &lt;a href=&quot;http://www.amazon.com/gp/product/B002VA813U/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B002VA813U&amp;amp;linkCode=as2&amp;amp;tag=wwwcrocodilorg&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;cheap USB temperature sensor&lt;/a&gt; and COSM submission:  &lt;a href=&quot;https://github.com/vzaliva/lmsensors-cosm&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;https://github.com/vzaliva/temper-python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;3. Data collection from &lt;a href=&quot;https://www.amazon.com/dp/B009GDHYPQ/ref=as_li_ss_til?tag=wwwcrocodilorg&amp;amp;camp=0&amp;amp;creative=0&amp;amp;linkCode=as4&amp;amp;creativeASIN=B009GDHYPQ&amp;amp;adid=0EP31VDSB7XE5EB7AENR&amp;amp;&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;NEST thermostat&lt;/a&gt; and COSM submission: &lt;a href=&quot;https://github.com/vzaliva/lmsensors-cosm&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;https://github.com/vzaliva/pynest&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;4. Data collection from Linux PC sensors (LMSENSORS) and COSM submission: &lt;a href=&quot;https://github.com/vzaliva/lmsensors-cosm&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;https://github.com/vzaliva/lmsensors-cosm&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to learn more about the project, you can read my preliminary &lt;a href=&quot;https://docs.google.com/file/d/0BzQSxooAoct2bU9XWUVsdHlYRGs/edit?usp=sharing&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;report&lt;/a&gt; or take a look at the &lt;a href=&quot;https://docs.google.com/presentation/d/1ldphDQ2nj1q1ALCq9SLoBsX6TonCJvm67iKGuOzZ1WA/edit?usp=sharing&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;presentation&lt;/a&gt;.</description>
  <comments>http://notbrainsurgery.livejournal.com/42157.html</comments>
  <category>temperature</category>
  <category>xively</category>
  <category>cosm</category>
  <category>sensor network</category>
  <category>lmsensors</category>
  <category>sensors</category>
  <category>cmu</category>
  <category>nest</category>
  <category>xbee</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/41806.html</guid>
  <pubDate>Thu, 28 Feb 2013 19:53:57 GMT</pubDate>
  <title>More on weather similarity </title>
  <link>http://notbrainsurgery.livejournal.com/41806.html</link>
  <description>I probably already mentioned &lt;a href=&quot;http://www.codeminders.com/weather_similarity/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;weather similarity&lt;/a&gt; service. Today I got curious: how the map of earth would look like if cities are organized by temperature similarity, no matter where they are located geographically. Below is a picture of what I got:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8515809351/&quot; title=&quot;weather similarity map by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8097/8515809351_70c8d6b095_c.jpg&quot; width=&quot;800&quot; height=&quot;591&quot; alt=&quot;weather similarity map&quot;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(I have labelled only cities with population over 2 million).&lt;br /&gt;&lt;br /&gt;&lt;i&gt;I wish I had a better way to show it in the browser. It would be nice to be able to zoom and show tooltips with city names. I have not find readily available JavaScript library for this and I do not have time to code this myself. If somebody interested in visualizing this I will be glad to share my data.&lt;/i&gt;</description>
  <comments>http://notbrainsurgery.livejournal.com/41806.html</comments>
  <category>2d</category>
  <category>temperature</category>
  <category>weather</category>
  <category>javascript</category>
  <category>similarity</category>
  <category>visualization</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/41495.html</guid>
  <pubDate>Thu, 22 Nov 2012 02:10:41 GMT</pubDate>
  <title>PPI fever</title>
  <link>http://notbrainsurgery.livejournal.com/41495.html</link>
  <description>Once you switch to &amp;quot;retina&amp;quot; display on MacBook Pro, there is no going back. You just could not stop seeing huge pixels and artifacts on lower resolution screens. So, naturally when buying various devices with screens you want to keep an eye on PPI (pixels per inch). Here are some numbers (in decreasing order):&lt;br /&gt;&lt;br&gt;&lt;br /&gt;Nokia Lumia 920 - 332 PPI&lt;br /&gt;iPhone 4/4S/5 Retina - 326 PPI&lt;br /&gt;Samsung Galaxy S3 - 306 PPI&lt;br /&gt;iPad 3 - 264 PPI&lt;br /&gt;MacBook Pro 15-inch Retina display - 220 PPI&lt;br /&gt;Amazon Kindle/Kindle Touch - 167 PPI&lt;br /&gt;Microsoft Surface - 148 PPI&lt;br /&gt;iPad 2 - 132 PPI&lt;br /&gt;MacBook Air 13-inch - 128 PPI&lt;br /&gt;&lt;br&gt;&lt;br /&gt;(More numbers &lt;a href=&quot;http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;here&lt;/a&gt;)</description>
  <comments>http://notbrainsurgery.livejournal.com/41495.html</comments>
  <category>retina</category>
  <category>ppi</category>
  <category>display</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/41401.html</guid>
  <pubDate>Thu, 22 Nov 2012 01:36:59 GMT</pubDate>
  <title>Scala class students in percent of their country population</title>
  <link>http://notbrainsurgery.livejournal.com/41401.html</link>
  <description>Few people at &lt;a href=&quot;http://www.codeminders.com/&quot; rel=&quot;nofollow&quot;&gt;our company&lt;/a&gt; took Coursera &lt;a href=&quot;https://www.coursera.org/course/progfun&quot; rel=&quot;nofollow&quot;&gt;&amp;quot;Functional Programming Principles in Scala&amp;quot;&lt;/a&gt; class. I think this is great when developers try to learn new languages and&amp;nbsp;technologies&amp;nbsp;and we actually pay a small bonus for taking such classes.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;In one of his &lt;a href=&quot;http://skillsmatter.com/podcast/scala/keynote-martin-odersky/ac-5894&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;presentations&lt;/a&gt; Martin Odersky has shown a slide showing a distribution of students per country (also shown on this &lt;a href=&quot;http://lampwww.epfl.ch/~hmiller/worldmap.html&quot; rel=&quot;nofollow&quot;&gt;map&lt;/a&gt;). Unfortunately it is not very useful as it is not adjusted for country population. So my fiend Alex wrote a small Scala program to parse JSON and extract the data, and I have used Mathematica to adjust it by country population. The table with results, could be seen &lt;a href=&quot;https://docs.google.com/spreadsheet/ccc?key=0AhWzm3JBPnPmdGlUekxfMTRDTFFfMzFtajJSalRqN0E&quot; rel=&quot;nofollow&quot;&gt;here&lt;/a&gt;. The bar chart showing top 40 countries in precent of their population taken the class looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://notbrainsurgery.livejournal.com/pics/catalog/453/1371&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;img alt=&quot;Scala Students&quot; height=&quot;846&quot; src=&quot;http://ic.pics.livejournal.com/notbrainsurgery/1461624/1371/1371_original.png&quot; title=&quot;Scala Students&quot; width=&quot;1369&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;USA is right behind Ukraine!</description>
  <comments>http://notbrainsurgery.livejournal.com/41401.html</comments>
  <category>codeminders</category>
  <category>scala</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/41168.html</guid>
  <pubDate>Wed, 07 Nov 2012 17:43:56 GMT</pubDate>
  <title>Amazon Instnat Video, Comcast and Google DNS</title>
  <link>http://notbrainsurgery.livejournal.com/41168.html</link>
  <description>As Amazon Prime Customer I have access to &lt;a href=&quot;www.amazon.com/piv&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Prime Instant Video&lt;/a&gt;. I have a PS3 with Amazon Instant Video application connected to my TV which I tried to use to watch some movies. Surprisingly the playback was freezing all the time and the application was complaining about insufficicent bandwidth. I have Comcast cable and &lt;a href=&quot;http://speedtest.net/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;SpeedTest&lt;/a&gt; clocks it at 30Mbps/5Mbps. Out of curiosity I tried instant video playback on my Mac and it has shown that it is getting only aroung 300Kbps of bandwidth.&lt;br /&gt;&lt;br /&gt;To make long story short the problem was in my DNS settings. I was using &lt;a href=&quot;https://developers.google.com/speed/public-dns/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Google Public DNS&lt;/a&gt; at home. Apparently this prevented Amazon CDN from properly detecting my location and streaming video from the closest server.  Once I have switched back to &lt;a href=&quot;http://dns.comcast.net/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Comcast DNS&lt;/a&gt;,  video playback speed has improved and I am now able to stream movies in full HD.&lt;br /&gt;&lt;br /&gt;I must report that after switching back to Comcast DNS I have noticed that my web page browsing become slower. Apparently Google DNS is indeed &lt;a href=&quot;https://developers.google.com/speed/public-dns/docs/performance&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;faster&lt;/a&gt;!</description>
  <comments>http://notbrainsurgery.livejournal.com/41168.html</comments>
  <category>amazon</category>
  <category>ps3</category>
  <category>comcast</category>
  <category>instantvideo</category>
  <category>dns</category>
  <category>google</category>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/40908.html</guid>
  <pubDate>Fri, 19 Oct 2012 19:29:27 GMT</pubDate>
  <title>city maps image analysis</title>
  <link>http://notbrainsurgery.livejournal.com/40908.html</link>
  <description>While reading recently about &lt;a href=&quot;http://www.brainpickings.org/index.php/2012/10/03/armelle-caron-everything-tidy/&quot; rel=&quot;nofollow&quot;&gt;Armelle Caron work&lt;/a&gt; on city maps deconstruction it occured to me that city maps could be analyzed using mathematical tools for image analysis. I started by looking looking at city grids of Paris and New York:&lt;br /&gt;&lt;br /&gt;Paris:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103416778/&quot; title=&quot;paris by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img alt=&quot;paris&quot; height=&quot;409&quot; src=&quot;http://farm9.staticflickr.com/8334/8103416778_9dcbebcbbe.jpg&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;New York:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103416696/&quot; title=&quot;New York by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img alt=&quot;New York&quot; height=&quot;344&quot; src=&quot;http://farm9.staticflickr.com/8185/8103416696_032776a512.jpg&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You can see lot of straight streets in NY while Paris is more curved. This could be more clearly seen by applying &lt;a href=&quot;http://en.wikipedia.org/wiki/Hough_transform&quot; rel=&quot;nofollow&quot;&gt;Hough Transform&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;Paris:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103383439/&quot; title=&quot;paris-hough by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8474/8103383439_9cca61bfd0.jpg&quot; width=&quot;500&quot; height=&quot;450&quot; alt=&quot;paris-hough&quot;&gt;&lt;/a&gt;&lt;br /&gt;New York:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103383513/&quot; title=&quot;ny-hough by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8052/8103383513_e6e737d316.jpg&quot; width=&quot;500&quot; height=&quot;450&quot; alt=&quot;ny-hough&quot;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(you can click on images for larger resolution versions)&lt;br /&gt;&lt;br /&gt;We can also explore spatial frequencies of these maps using &lt;a href=&quot;http://homepages.inf.ed.ac.uk/rbf/HIPR2/fourier.htm&quot; rel=&quot;nofollow&quot;&gt;Discrete Fourier transform&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;Paris:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103383603/&quot; title=&quot;paris-fourier by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8052/8103383603_d35067bd90.jpg&quot; width=&quot;500&quot; height=&quot;405&quot; alt=&quot;paris-fourier&quot;&gt;&lt;/a&gt;&lt;br /&gt;New York:&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/8103398224/&quot; title=&quot;ny-fourier by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://farm9.staticflickr.com/8054/8103398224_1e44d7b1dd.jpg&quot; width=&quot;500&quot; height=&quot;405&quot; alt=&quot;ny-fourier&quot;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I will not elaborate here how these images should be interpreted. If you are unfamiliar with them and interested to learn more you can read about these transformations which are well described in many other sources.</description>
  <comments>http://notbrainsurgery.livejournal.com/40908.html</comments>
  <category>image analysis</category>
  <category>mathematics</category>
  <category>fourier</category>
  <category>hough</category>
  <category>map</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/40465.html</guid>
  <pubDate>Tue, 12 Jun 2012 23:08:16 GMT</pubDate>
  <title>Horizontal Perspective Correction in Text Images</title>
  <link>http://notbrainsurgery.livejournal.com/40465.html</link>
  <description>&lt;div&gt;I was recently working on a computer vision problem which I would like to share with you. We were dealing with images of a restaurant menu containing mostly text. Images were taken with a mobile phone camera while the user held the menu in his hand. Typically the image captures just part of the page (page boundaries were not necessarily included). The challenge was to perform perspective correction on such images. We treated this as a practical, rather than an academic problem and thus did not aim to get the perfect solution, so we were ready to accept one offering only a partial improvement.&lt;/div&gt;&lt;div&gt;The main, hopefully new, contributions of our approach are:&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Partial, horizontal-only perspective correction.&lt;/li&gt;&lt;li&gt;A new, statistical approach of choosing a horizontal vanishing point.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;The problem of perspective correction is well studied. Please see references at the end of this post for related work. The basic idea is as follows: Given a pair of parallel lines, we can find a vanishing point. In a non-distorted image, all vanishing points lie at the line at infinity[1]. However, in a image projection, parallel lines may intersect at a real point. Given two pairs of lines which are supposed to be parallel (before projection), we can find two vanishing points which would define a line at infinity after perspective distortion. The idea is illustrated below. Vh and Vv are horizontal and vertical vanishing points respectively.&lt;/div&gt;&lt;div style=&quot;text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; height=&quot;298&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/vp.png&quot; style=&quot;border-width: 0px; border-style: solid; &quot; title=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/div&gt;&lt;div&gt;Now, working in homogeneous coordinates [5], we can build a homography [7], which would translate this line to an ideal line at infinity. Applying this homography to all pixels of a perspective-distorted image would allow us to reconstruct the original.&amp;nbsp;&lt;/div&gt;&lt;div&gt;Our first task is to find two pairs of line in the distorted imags which were parallel in the original image. One obvious idea is to find horizontal lines corresponding to lines of printed text. The intersection of two such lines would give us a horizontal vanishing point. For the type of images we are dealing with, unfortunately there is a no easy way to detect vertical lines. In absence of vertical page margins in typical images, there are simply not enough vertical features with which to align such lines. So we must resign ourselves to partial perspective correction, using the horizontal vanishing point alone. This is done by assuming that the vertical vanishing point is located at infinity in the positive direction of the y axis. The coordinate of such a point in homogeneous coordinates would be (0,1,0), with the last zero indicating an ideal point (point at infinity).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Starting with a pre-processed image (already converted to black and white, we will apply Hough transform [6] to detect straight lines. Because we are interested in horizontal lines, assuming that we are not dealing with extreme cases of images being significantly distorted, we can limit the line angles that we consider to +-Pi/3 from the x axis. The transformation could be applied to a scaled down image, provided that the aspect ratio is preserved. This would help us to speed up the computations. Next, we will threshold the results of the Hough transform and convert the resulting detected lines from polar to homogeneous coordinates. As a result, we will have a small set of potentially horizontal lines. In theory any pair of them should suffice, but in practice, some detected lines may not correspond to horizontal lines of text, but represent a noise or other image features.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;To select the two most suitable lines from this set, we will use a statistical approach. First we will build a pairwise intersection of all lines from the set. In homogeneous coordinates, the intersection of two lines is a cross product of their coordinate vectors. This gives us a set of a potential horizontal vanishing points. We will filter this set excluding points at infinity, points falling within the bounding rectangle of the projected image, and to exclude extreme cases of perspective distortion, the points located too close to the center of coordinates in the horizontal direction. The &amp;ldquo;too close&amp;rdquo; criteria is expressed as a threshold on an absolute value of the &amp;nbsp;ratio &amp;nbsp;of the x coordinate of a potential horizontal vanishing point to the image width. &amp;nbsp;Each of the remaining points could be used to calculate a homography used to perform horizontal perspective correction:&lt;/div&gt;&lt;div style=&quot;text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/Hp.png&quot; style=&quot;border-width: 0px; border-style: solid; &quot; title=&quot;&quot; width=&quot;150&quot; /&gt;&lt;/div&gt;&lt;div&gt;where (a,b,c) is a projection of the line at infinity calculated as the cross product of Vx and Vy=(0,1,0). As a result of such correction, the two lines used to produce a chosen point will become parallel. Our working assumption is that because of the regular text line structure of the original image, most of the lines we detected were parallel. This allows us to define an evaluation metric of suitability for a homography as the standard deviation of the angles between all corrected lines and the x axis. The vanishing point producing the minimal standard deviation will correspond to the transformation which makes the set of lines the closest to parallel.&lt;/div&gt;&lt;div&gt;The resulting homography performs perspective correction, making our set of original horizontal lines to become closer to parallel after transformation. However, this does not make them actually horizontal. We need an additional affine rotation homography to achieve this. After perspective transformation, we will take the mean value of angles between lines in our set and &amp;nbsp;the x axis as a rotation angle theta. The homography representing &amp;nbsp;this rotation would be:&lt;/div&gt;&lt;div style=&quot;text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; height=&quot;53&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/Ha.png&quot; style=&quot;border-width: 0px; border-style: solid; &quot; title=&quot;&quot; width=&quot;200&quot; /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;The final homography combining perspective correction and rotation is the combination of the two transformations&lt;/div&gt;&lt;div style=&quot;text-align: center; &quot;&gt;H = Ha Hp&lt;/div&gt;&lt;div&gt;&lt;br /&gt;which coincidentally have the form:&lt;/div&gt;&lt;div style=&quot;text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/Hm.png&quot; style=&quot;border-width: 0px; border-style: solid; &quot; title=&quot;&quot; width=&quot;200&quot; /&gt;&lt;/div&gt;&lt;div&gt;Sample results of the algorithm are shown below. Lines used for horizontal correction are shown in orange. The original image: &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/o.jpg&quot; title=&quot;&quot; width=&quot;600&quot; /&gt;&lt;br /&gt;&lt;br /&gt;The corrected image:&lt;br /&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; src=&quot;http://i168.photobucket.com/albums/u180/vzaliva/c.jpg&quot; title=&quot;&quot; width=&quot;600&quot; /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style=&quot;font-size:larger;&quot;&gt;&lt;b&gt;References&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;[1] R. Hartley and A. Zisserman, &amp;quot;&lt;i&gt;A multiple view geometry in computer vision&amp;quot;&lt;/i&gt;, Second Edition, Cambridge University Press , 2004.&lt;/div&gt;&lt;div&gt;[2] P. Clark, &amp;ldquo;&lt;i&gt;Estimating the orientation and recovery of text planes in a single image&lt;/i&gt;,&amp;rdquo; Proceedings of the 12th British Machine, 2001.&lt;/div&gt;&lt;div&gt;[3] V. Cantoni, L. Lombardi, and M. Porta, &amp;ldquo;&lt;i&gt;Vanishing point detection: Representation analysis and new approaches&lt;/i&gt;,&amp;rdquo; Image Analysis and, no. Iciap, 2001.&lt;/div&gt;&lt;div&gt;[4] L. Jagannathan, &amp;ldquo;&lt;i&gt;Perspective correction methods for camera based document analysis,&lt;/i&gt;&amp;rdquo; on Camera-based Document Analysis and, pp. 148-154, 2005.&lt;/div&gt;&lt;div&gt;[5] Wikipedia contributors. &lt;i&gt;Homogeneous coordinates&lt;/i&gt;. Wikipedia, The Free Encyclopedia. May 22, 2012, 07:12 UTC. Available at: &lt;a href=&quot;http://en.wikipedia.org/w/index.php?title=Homogeneous_coordinates&amp;amp;oldid=493787558&quot; rel=&quot;nofollow&quot;&gt;http://en.wikipedia.org/w/index.php?title=Homogeneous_coordinates&amp;amp;oldid=493787558&lt;/a&gt;. Accessed June 12, 2012.&lt;/div&gt;&lt;div&gt;[6] Wikipedia contributors. &lt;i&gt;Hough transform&lt;/i&gt;. Wikipedia, The Free Encyclopedia. June 11, 2012, 00:12 UTC. Available at: &lt;a href=&quot;http://en.wikipedia.org/w/index.php?title=Hough_transform&amp;amp;oldid=496983042&quot; rel=&quot;nofollow&quot;&gt;http://en.wikipedia.org/w/index.php?title=Hough_transform&amp;amp;oldid=496983042&lt;/a&gt;. Accessed June 12, 2012.&lt;/div&gt;&lt;div&gt;[7] Wikipedia contributors. &lt;i&gt;Homography&lt;/i&gt;. Wikipedia, The Free Encyclopedia. March 12, 2012, 06:33 UTC. Available at: &lt;a href=&quot;http://en.wikipedia.org/w/index.php?title=Homography&amp;amp;oldid=481473030&quot; rel=&quot;nofollow&quot;&gt;http://en.wikipedia.org/w/index.php?title=Homography&amp;amp;oldid=481473030&lt;/a&gt;. Accessed June 12, 2012.&lt;/div&gt;</description>
  <comments>http://notbrainsurgery.livejournal.com/40465.html</comments>
  <category>computer vision</category>
  <category>perspective correction</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/40308.html</guid>
  <pubDate>Sat, 25 Feb 2012 02:18:51 GMT</pubDate>
  <title>time based parental control for web sites</title>
  <link>http://notbrainsurgery.livejournal.com/40308.html</link>
  <description>My 9 y.o kid is using Internet. I see nothing wrong with that. Internet is no longer an optional part of this world and he needs to be friendly with it. He reads Wikipedia, visits LEGO-related sites, etc. I have enabled parental controls on his Mac, so hopefully some inappropriate web sites are blocked. The only problem I have is amount of time he spends on YouTube. YouTube could be an amazing educational resource but at the same time it could be an amazing time waster. I decided to limit amount of time he spends on YouTube, like many parents limit amount of time their kids spend in front on TV. I found no easy way to do this short of blocking his complete access to computer according to pre-defined schedule. But I wanted to block only YouTube and let him use everything else. Below I will share my solution to the problem. It is a bit involved, and requires you to have Linux box on your home network, but this is the only working solution I found so far.&lt;br /&gt;&lt;br /&gt;The trick is to use HTTP proxy (Squid) with squidguard plugin. On RHEL you can install and activate required packages&amp;nbsp;using following commands:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;yum install squid squidguard squidguard-blacklists&lt;br /&gt;chkconfig squid on&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Let us start with default config file:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;cd /etc/squid&lt;br /&gt;cp squidguard-blacklists.conf squidguard.conf&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now edit squidguard.conf, adding following lines at the bottom:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;time playhours {&lt;br /&gt;&lt;b&gt;weekly * 19:00-20:00 &lt;/b&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;src all {&lt;br /&gt;ip &lt;b&gt;10.0.81.0/24&lt;/b&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;### ACL definition&lt;br /&gt;acl {&lt;br /&gt;all within playhours {&lt;br /&gt;pass good !bad !adult !aggressive !hacking !warez any&lt;br /&gt;redirect 302:http://localhost/access-denied.html?url=%u&lt;br /&gt;} else {&lt;br /&gt;pass good !bad !adult !aggressive !audio-video !hacking !warez any&lt;br /&gt;redirect 302:http://localhost/access-denied.html?url=%u&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;default {&lt;br /&gt;pass none&lt;br /&gt;redirect http://localhost/block.html&lt;br /&gt;}&lt;br /&gt;}&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;My home network is &lt;i&gt;10.0.81.0/24&lt;/i&gt;. This config allows to access video web sites (which include YouTube) only&lt;br /&gt;during 1 hour: from 7pm to 8pm every day. You can devise more complex schedule if you want to. In addition&lt;br /&gt;to blocking YouTube this config blocks various adult and hacking web sites at all times, as extra level of protection.&lt;br /&gt;&lt;br /&gt;To test your config you can use this handy command:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;echo &amp;quot;http://www.youtube.com 10.0.81.1/ - - GET&amp;quot; | sudo -u squid squidGuard -c /etc/squid/squidguard.conf -d&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;To activate it, edit squid.conf adding the following line:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;url_rewrite_program /usr/bin/squidGuard -c /etc/squid/squidguard.conf&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Now all you need to set up your child computer to use HTTP proxy, pointing to IP of squid computer and default&lt;br /&gt;port 3128. Unfortunately due to the bug in MacOS versions up to Lion you need to disable parental controls on the account to make proxy settings work.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;As an extra measure of protection, you can use &lt;a href=&quot;http://opendns.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;OpenDNS&lt;/a&gt; free parental controls. Here is how my OpenDNS dashboard&amp;nbsp;looks like:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/6781171450/&quot; title=&quot;Screen Shot 2012-02-24 at 17.48.48 by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img alt=&quot;Screen Shot 2012-02-24 at 17.48.48&quot; height=&quot;532&quot; src=&quot;http://farm8.staticflickr.com/7050/6781171450_9b723140fb_z.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After setting up OpenDNS account all you need to do is to add the following line to squid.conf:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;dns_nameservers 208.67.220.220 208.67.222.222&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Hint: if you have dynamic IP, you can use ddclient to update it at OpenDNS site using &lt;a href=&quot;http://forums.opendns.com/comments.php?DiscussionID=8869http://forums.opendns.com/comments.php?DiscussionID=8869&amp;lt;br /&amp;gt;http://forums.opendns.com/comments.php?DiscussionID=8869&amp;lt;br /&amp;gt;&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;this config&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And here is what is shown if YouTube is accessed during not permitted time slot:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/vzaliva/6927288897/&quot; title=&quot;Screen Shot 2012-02-24 at 17.50.49 by vzaliva, on Flickr&quot; rel=&quot;nofollow&quot;&gt;&lt;img alt=&quot;Screen Shot 2012-02-24 at 17.50.49&quot; height=&quot;334&quot; src=&quot;http://farm8.staticflickr.com/7193/6927288897_1d8c9481a6.jpg&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;</description>
  <comments>http://notbrainsurgery.livejournal.com/40308.html</comments>
  <category>squid</category>
  <category>opendns</category>
  <category>squidguard</category>
  <category>kids</category>
  <category>parental</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/40142.html</guid>
  <pubDate>Sun, 21 Aug 2011 21:27:59 GMT</pubDate>
  <title>Mobile user interface</title>
  <link>http://notbrainsurgery.livejournal.com/40142.html</link>
  <description>Designing mobile user interface is not very complex. Just use common sense, think about user, and keep an eye on limited screen estate. But, some people just do not get it. For example, Foursquare Android App, which I am using daily,  is driving me nuts. Take a look at the first screen I see when it is started:&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://img14.imageshack.us/img14/4800/snap20110821124834.png&quot; alt=&quot;4sq screenshot&quot;&gt;&lt;br /&gt;&lt;br /&gt;It opens in &quot;Friends&quot; tab, which supposed to show me where my friends are. OK. Makes sense. What do I see:&lt;br /&gt;&lt;br /&gt;1. It shows me where I am. Doh! I know where I am. No need to remind me. I want to see my friends, and given limited screen space it does not make sense to waste it on reminding me where I am now.&lt;br /&gt;&lt;br /&gt;2. It shows me same information again, this time under &quot;Last 3 hours&quot; section.  This is just plain stupid.&lt;br /&gt;&lt;br /&gt;At this point, 1/3 of screen space which could have been used to show my friend’s locations have been wasted just to remind me that I am Starbucks where I have checked in just 2 minutes ago. Taking into account additional space used by three levels of toolbars on top, just enough useful screen space left to shows me the locations for only two of my friends.</description>
  <comments>http://notbrainsurgery.livejournal.com/40142.html</comments>
  <category>ui</category>
  <category>foursquare</category>
  <category>user interface</category>
  <category>4sq</category>
  <category>mobile</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/39904.html</guid>
  <pubDate>Tue, 21 Jun 2011 23:03:26 GMT</pubDate>
  <title>growing money</title>
  <link>http://notbrainsurgery.livejournal.com/39904.html</link>
  <description>There is a lot of buzz about &lt;a href=&quot;http://en.wikipedia.org/wiki/Bitcoin&quot; rel=&quot;nofollow&quot;&gt;BitCoin&lt;/a&gt; in the news recently. Most people are exited about distributed, untraceable, and unregulated nature of this new currency. However, there is another overlooked aspect of it, related to the practice of &amp;quot;&lt;a href=&quot;https://en.bitcoin.it/wiki/How_bitcoin_works#Bitcoin_mining&quot; rel=&quot;nofollow&quot;&gt;bitcoin mining.&lt;/a&gt;&amp;quot; Anybody now can buy some hardware and start minting his own bitcoins. Initial investment in hardware aside, all you need now is to spend some electricity to produce money (bitcoins). Depending on your electricity costs and bitcoin exchange rates you can make small but steady monthly income.&lt;br /&gt;&lt;br /&gt;An interesting aspect of this electricity-to-money conversion is that electricity does not need to be transferred far and could be consumed very close to a generator producing it. For example, you can have a module with solar battery on top and bitcoin-generating computer inside which you can put anywhere to get free money literally growing under a sun!&lt;br /&gt;&lt;br /&gt;For example, the picture below shows &lt;a href=&quot;http://cncinco.en.made-in-china.com/product/zqEndIslsYpx/China-1000w-Solar-Photovoltic-System-Solar-Kit-For-Home-Use.html&quot; rel=&quot;nofollow&quot;&gt;1000w Solar Photovoltic System&lt;/a&gt;. It generates enough electricity to power a PC with 2-4 powerful GPU cards, capable of producing very decent bitcoin mining performance.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img width=&quot;320&quot; height=&quot;240&quot; alt=&quot;&quot; src=&quot;http://image.made-in-china.com/2f0j00ICQTmiFlOrpE/1000w-Solar-Photovoltic-System-Solar-Kit-For-Home-Use.jpg&quot; /&gt;</description>
  <comments>http://notbrainsurgery.livejournal.com/39904.html</comments>
  <category>bitcoin</category>
  <category>solar</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/39543.html</guid>
  <pubDate>Mon, 28 Feb 2011 02:50:59 GMT</pubDate>
  <title>Stolen laptop registry</title>
  <link>http://notbrainsurgery.livejournal.com/39543.html</link>
  <description>&amp;nbsp;My daughter is a UCSC student. Recently, her MacBook was stolen from her dorm room. She has filed a burglary report with campus police, but recovery chances are slim. Not to slander character of UC students, but I think there is a good chance the laptop will resurface somewhere on campus someday. Once it connects to the campus network it could be identified by MAC address. From there, hopefully, the identity of thief could be tracked down. I was able to find MAC address of stolen laptop, and my daughter gave it to campus IT. However, I am not sure if they would take the task of tracking stolen laptops seriously. &lt;br /&gt;&lt;br /&gt;This made me think that there might be a crowd sourced solution to this problem. Imagine a site, where one can report stolen computer along with its MAC address. Now, the volunteers would run a small daemon on their laptops. The daemon will be constantly monitoring the ARP table and check if any of nearby MAC addresses are in the list belonging to stolen machines. If match is detected a report along with spotting time and the location is sent to the owner. If enough people participate miscreants could be spotted on campuses, in hotels, at offices, cafes. This should work for all WiFi or ethernet equipped devices: computers, laptops, tables, mobile phones, game consoles, etc.&lt;br /&gt;&lt;br /&gt;Obviously, there are some privacy issues. Sending somewhere MAC addresses of nearby computers could be viewed by some people as a privacy violation. This could be addressed by using one-way hashes and sending them instead of MAC addresses. Second concern is what if somebody puts into the system a MAC address of somebody else laptop with purpose to track this person. This issue is more difficult to address. Maybe the site should require submitters to provide their real identity (verified by credit card of by other reliable means) or accept new records only if police report has been filed and a copy has been enclosed.</description>
  <comments>http://notbrainsurgery.livejournal.com/39543.html</comments>
  <category>idea</category>
  <category>theft</category>
  <category>registry</category>
  <category>stolen</category>
  <category>laptop</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/39281.html</guid>
  <pubDate>Sat, 05 Feb 2011 18:31:55 GMT</pubDate>
  <title>godaddy backup with rsync over ssh</title>
  <link>http://notbrainsurgery.livejournal.com/39281.html</link>
  <description>I have recently moved one of my web servers containing mostly static pages to &lt;a href=&quot;http://www.godaddy.com/&quot; rel=&quot;nofollow&quot;&gt;GoDaddy&lt;/a&gt; hosting. Before it was hosted at Linux machine and I was backing it up using weekly cron sript using rsync. With new hosting I had to set up a new backup procedure. The problem us that although GoDaddy &lt;a href=&quot;http://help.godaddy.com/article/4942&quot; rel=&quot;nofollow&quot;&gt;gives you SSH&lt;/a&gt; access they do not provide &lt;a href=&quot;http://en.wikipedia.org/wiki/Rsync&quot; rel=&quot;nofollow&quot;&gt;rsync&lt;/a&gt; on their machines. I was toying with idea of cross-compiling it elsewhere and bringing a binary with me, but I found a simpler solution using &lt;a href=&quot;http://fuse.sourceforge.net/sshfs.html&quot; rel=&quot;nofollow&quot;&gt;sshfs&lt;/a&gt;. Here are step by step instruciton:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Definitions:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;www.yourhost.com&lt;/i&gt; - your go daddy web host (assuming it has ssh access)&lt;br /&gt;&lt;i&gt;username&lt;/i&gt; - your login name for ssh access to go daddy host&lt;br /&gt;&lt;i&gt;backuphost&lt;/i&gt;-  a Linux machine with root access, at which you will be backing up your web site.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;on backup host:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. As root, generate new ssh key without passphrase using &lt;i&gt;ssh-keygen&lt;/i&gt; command. Call it &lt;i&gt;backup.dsa&lt;/i&gt;. (normally stored in &lt;i&gt;/root/.ssh/backup.dsa&lt;/i&gt;)&lt;br /&gt;&lt;br /&gt;2. Add the following lines to your &lt;i&gt;/root/.ssh/config&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Host www.yourhost.com&lt;br /&gt;   User username&lt;br /&gt;   IdentityFile ~/.ssh/backup.dsa&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;3. Make sure you have empty mount point (&lt;i&gt;mkdir /mnt&lt;/i&gt;)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;on www.yourhost.com:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. Add contant of the file backup.dsa.pub (one you generated on backup host) to &lt;i&gt;~/.ssh/authorized_keys&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Now we are all set. Let us try it:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First just try to see if your ssh works. As root issue the command:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;ssh www.yourhost.com&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;It should let you in without password. If this does not work add -v flag to ssh command and proceed with regular troubleshooting of ssh public key authentication. Most likely you will need to fix permissions on ~/.ssh directory and its contents on the remote host.&lt;br /&gt;&lt;br /&gt;Once this worked, you can mount remote filesytem:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;sshfs www.yourhost.com: /mnt&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;And back it up:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;rsync -az -qq /mnt/html/  ~/backups/www.yourhost.com&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Do not forget to unmount when you done:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;umount /mnt&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;This is a quick and dirty solution. Most likely &lt;i&gt;fusefs&lt;/i&gt; options could be tweaked to get even better performance. You can add these commands to script file in /etc/cron.weekly/ on backup host to implement automatic weekly backups.</description>
  <comments>http://notbrainsurgery.livejournal.com/39281.html</comments>
  <category>godaddy</category>
  <category>ssh</category>
  <category>linux</category>
  <category>backup</category>
  <category>rsync</category>
  <category>fusefs</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/39052.html</guid>
  <pubDate>Tue, 25 Jan 2011 23:59:27 GMT</pubDate>
  <title>Android keyboard</title>
  <link>http://notbrainsurgery.livejournal.com/39052.html</link>
  <description>Switching between Nexus One and iPhone I could not help noticing that my typing error rate is significantly lower with iPhone. Both devices have about the same screen size and similar keyboard layout. That made me wondering what makes iPhone keyboard superior. Is it the layout, for example spacing between the keys? Smarter algorithms? With help from &lt;a href=&quot;http://www.quora.com/How-is-the-Nexus-One-Android-on-screen-keyboard-different-from-the-iPhones&quot; rel=&quot;nofollow&quot;&gt;Quora&lt;/a&gt; I have been able to disover that one of the key differences is an algorithm used by iPhone to adjust target area based on next key prediction:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&quot;Although the size of the keys displayed on an iPhone/iPod Touch keyboard does not change during typing, the iPhone&apos;s predictive algorithm will subtly increase the &quot;target area&quot; of what it thinks your next keystroke will be. This helps reduce mistypes.&quot;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target=&quot;_blank&quot; title=&quot;ImageShack - Image And Video Hosting&quot; href=&quot;http://img228.imageshack.us/i/screenshot20110125at155.png/&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://img228.imageshack.us/img228/9067/screenshot20110125at155.png&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Alternative Android keyboard called &lt;a href=&quot;http://phandroid.com/2010/03/23/thickbuttons-another-android-keyboard-alternative/&quot; rel=&quot;nofollow&quot;&gt;TickButtons&lt;/a&gt; using similar idea, except it does change visual size of the keys:&lt;br /&gt;&lt;br /&gt;&lt;a target=&quot;_blank&quot; title=&quot;ImageShack - Image And Video Hosting&quot; href=&quot;http://img442.imageshack.us/i/thickbuttons.jpg/&quot; rel=&quot;nofollow&quot;&gt;&lt;img src=&quot;http://img442.imageshack.us/img442/3741/thickbuttons.jpg&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Luckly there are many alternative keyboards available for Android. I was looking for a conventional keyboard, so I have exluded ones like &lt;a href=&quot;http://swypeinc.com/&quot; rel=&quot;nofollow&quot;&gt;Swype&lt;/a&gt;. After trying several of them I was able to find one which allows me to type much more accurately on Android. Not only it supports target area resizing as iPhone does, it also have a calibration mode where you train it to the way you are pressing the keys. I am now happy user of &lt;a href=&quot;http://www.dexilog.com/smartkeyboard/&quot; rel=&quot;nofollow&quot;&gt;Smart Keyboard&lt;/a&gt;.</description>
  <comments>http://notbrainsurgery.livejournal.com/39052.html</comments>
  <category>typing</category>
  <category>algorithm</category>
  <category>predictive</category>
  <category>android</category>
  <category>iphone</category>
  <category>keyboard</category>
  <lj:security>public</lj:security>
  <lj:reply-count>3</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/38673.html</guid>
  <pubDate>Mon, 17 Jan 2011 01:16:34 GMT</pubDate>
  <title>smarter car navigation</title>
  <link>http://notbrainsurgery.livejournal.com/38673.html</link>
  <description>Here is the situation: you are driving a car, perhaps using your GPS device for navigation and want to stop for a quick bite or a cup of coffee. You would probably first think about using list of nearby restaurants offered by your GPS device, or perhaps an application like Yelp to see what are the good places to eat nearby. The problem is that location search offered by most applications is not particularly optimized for such simple and common situation. The gist of the problem is that it is assuming that you are static and does not use information about your route and roads into account.&lt;br /&gt;&lt;br /&gt;For example it would show you all restaurants in, say, 5 mile vicinity from whenever you are. But most likely you will not be interested in the restaurants which are located behind you. You would rather not spend time turning around and going back. So in simple case your search area should be shaped not like a circle with radius of 5 miles with you in the center, but rather as a rectangle extending along your route with some reasonable distance on both sides. This would make your search aware of your route.&lt;br /&gt;&lt;br /&gt;In more general case I wish my navigation system to be clever enough to search in a &amp;ldquo;detour&amp;rdquo; mode, assuming than whatever I am searching for is just a stop on my path and I would want to continue to my ultimate destination afterwards. In this case it could use different distance metric to filter and order search results. Now it orders them just by distance from my location. What I really care is how much detour to given place will extend my route. For example, let us call my current position A and my final destination Z. There are two potential restaurants, say, B and C which fit my search criteria (Indian food most likely, in my case). Instead of using distance AB and AC it should build a route A-B-Z and A-C-Z and compare overall time from A to Z over each of these routes. It would be especially nice if it would use traffic information, if available.</description>
  <comments>http://notbrainsurgery.livejournal.com/38673.html</comments>
  <category>yelp</category>
  <category>gps</category>
  <category>navigation</category>
  <category>car</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/38622.html</guid>
  <pubDate>Fri, 05 Nov 2010 21:15:16 GMT</pubDate>
  <title>OWI Robotic Arm Edge USB protocol (and sampe code)</title>
  <link>http://notbrainsurgery.livejournal.com/38622.html</link>
  <description>In my &lt;a href=&quot;http://notbrainsurgery.livejournal.com/38215.html&quot; rel=&quot;nofollow&quot;&gt;last post&lt;/a&gt; I have described OWI-007 Robotic Arm parallel port protocol. They have a newer, more advanced model called &lt;a href=&quot;http://www.owirobot.com/products/Robotic-Arm-Edge.html&quot; rel=&quot;nofollow&quot;&gt;Robotic Arm Edge&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://img220.imageshack.us/img220/5844/roboticarmedge.jpg&quot; alt=&quot;Robotic Arm Edge&quot;&gt;&lt;br /&gt;&lt;br /&gt;You can use &lt;a href=&quot;http://www.owirobot.com/products/USB-Interface-for-Robotic-Arm-Edge.html&quot; rel=&quot;nofollow&quot;&gt;USB interface module&lt;/a&gt; to connect it to PC. Unfortunately official software works only under Windows and do not provide API. I have been able to reverse engineer their USB protocol and write sample code which allows to control the arm from Mac and Linux.&lt;br /&gt;&lt;br /&gt;This is USB device with vendor id 0x1267 and product id 0. The device is controlled by 3 byte commands sent via USB control transfers. Command format is byte0, byte1, byte2. Each byte controls a group of arm features. All motors could be controlled independently. Most commands start action which is continued until next action is signalled. Byte &apos;00&apos; universally used as stop action.&lt;br /&gt;&lt;br /&gt;Bit numbering in byte:&lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; 7 (most significant) &lt;/td&gt;&lt;td&gt; 6 &lt;/td&gt;&lt;td&gt; 5 &lt;/td&gt;&lt;td&gt; 4 &lt;/td&gt;&lt;td&gt; 3 &lt;/td&gt;&lt;td&gt; 2 &lt;/td&gt;&lt;td&gt; 1 &lt;/td&gt;&lt;td&gt; 0 (least significant) &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Meaning of  bits in each byte:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Byte 0&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First byte controls grip, shoulder and wrist. Bits are assigned as following:&lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Bit numbers&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Controls&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Bit combinations&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 0,1 &lt;/td&gt;&lt;td&gt; Grip &lt;/td&gt;&lt;td&gt; 00-do not move, 01-close, 10-open &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 2,3 &lt;/td&gt;&lt;td&gt; Wrist &lt;/td&gt;&lt;td&gt; 00-do not move, 01-move up, 10-move down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 4,5 &lt;/td&gt;&lt;td&gt; Elbow &lt;/td&gt;&lt;td&gt; 00-do not move, 01-move up, 10-move down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 6,7 &lt;/td&gt;&lt;td&gt; Shoulder &lt;/td&gt;&lt;td&gt; 00-do not move, 01-move up, 10-move down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt; &lt;br /&gt;Sample commands, activating single motor: &lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Byte 0&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Command&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 00 &lt;/td&gt;&lt;td&gt; stop grip, wrist, elbow and shoulder movement &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 01 &lt;/td&gt;&lt;td&gt; grip close &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 02 &lt;/td&gt;&lt;td&gt; grip open &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 04 &lt;/td&gt;&lt;td&gt; wrist up &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 08 &lt;/td&gt;&lt;td&gt; wrist down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 10 &lt;/td&gt;&lt;td&gt; elbow up &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 20 &lt;/td&gt;&lt;td&gt; elbow down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 40 &lt;/td&gt;&lt;td&gt; shoulder up &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 80 &lt;/td&gt;&lt;td&gt; shoulder down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Sample commands activating 2 motors simultaneously:&lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Byte 0&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Command&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 11 &lt;/td&gt;&lt;td&gt; grip close and elbow up&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 82 &lt;/td&gt;&lt;td&gt; grip open and shoulder down &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Byte1&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Second byte controls base. &lt;br /&gt;Bits are assigned as following:&lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Bit numbers&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Controls&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Bit combinations&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 0,1 &lt;/td&gt;&lt;td&gt; Base &lt;/td&gt;&lt;td&gt; 00-do not move, 01-rotate clockwise, 10-rotate counter-clocwise &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Sample commands: &lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Byte 1&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Command&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 00 &lt;/td&gt;&lt;td&gt; stop base rotation &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 01 &lt;/td&gt;&lt;td&gt; rotate base clockwise &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 02 &lt;/td&gt;&lt;td&gt; rotate base counter-clocwise &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Byte 2&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Third byte controls LED light inside the grip.&lt;br /&gt;Bits are assigned as following:&lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Bit numbers&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Controls&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Bit combinations&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 0 &lt;/td&gt;&lt;td&gt; LED &lt;/td&gt;&lt;td&gt; 0-off, 1-on &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Sample commands: &lt;br /&gt;&lt;br /&gt;&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt; &lt;strong&gt;Byte 2&lt;/strong&gt; &lt;/td&gt;&lt;td&gt; &lt;strong&gt;Command&lt;/strong&gt; &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 00 &lt;/td&gt;&lt;td&gt; LED off &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt; 01 &lt;/td&gt;&lt;td&gt; LED on &lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Sample source code using &lt;a href=&quot;http://www.libusb.org/&quot; rel=&quot;nofollow&quot;&gt;libusb&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

#include &amp;lt;libusb-1.0/libusb.h&amp;gt;

#define EP_INTR	(1 | LIBUSB_ENDPOINT_IN)

#define ARM_VENDOR       0x1267
#define ARM_PRODUCT      0
#define CMD_DATALEN      3

libusb_device * find_arm(libusb_device **devs)
{
	libusb_device *dev;
	int i = 0;

	while ((dev = devs[i++]) != NULL) {
		struct libusb_device_descriptor desc;
		int r = libusb_get_device_descriptor(dev, &amp;amp;desc);
		if (r &amp;lt; 0) {
			fprintf(stderr, &amp;quot;failed to get device descriptor&amp;quot;);
			return NULL;
		}
		if(desc.idVendor == ARM_VENDOR &amp;amp;&amp;amp;
		   desc.idProduct == ARM_PRODUCT)
		  {
		    return dev;
		  }
	}
	return NULL;
}

int main(int ac, char **av)
{
    if(ac!=4)
    {
        fprintf(stderr,&amp;quot;Usage: armedgetest CMD0 CMD1 CMD2\n&amp;quot;);
        return 1;
    }

    unsigned char cmd[3];

    cmd[0]=(unsigned char)strtol(av[1],NULL,16);
    cmd[1]=(unsigned char)strtol(av[2],NULL,16);
    cmd[2]=(unsigned char)strtol(av[3],NULL,16);
    
	libusb_device **devs;
    libusb_device *dev;
    struct libusb_device_handle *devh = NULL;
	int r;
	ssize_t cnt;

	r = libusb_init(NULL);
	if (r &amp;lt; 0)
    {
	    fprintf(stderr, &amp;quot;failed to initialize libusb\n&amp;quot;);
	    return r;
    }

    libusb_set_debug(NULL,2);
    
	cnt = libusb_get_device_list(NULL, &amp;amp;devs);
	if (cnt &amp;lt; 0)
		return (int) cnt;
    dev=find_arm(devs);
    if(!dev)
    {
	    fprintf(stderr, &amp;quot;Robot Arm not found\n&amp;quot;);
	    return -1;
    }

    r = libusb_open(dev,&amp;amp;devh);
    if(r!=0)
    {
	    fprintf(stderr, &amp;quot;Error opening device\n&amp;quot;);
       	    libusb_free_device_list(devs, 1);
             libusb_exit(NULL);
	    return -1;
    }


    fprintf(stderr, &amp;quot;Sending %02X %02X %02X\n&amp;quot;,
            (int)cmd[0],
            (int)cmd[1],
            (int)cmd[2]
    );

    int actual_length=-1;
    
    r = libusb_control_transfer(devh,
                                0x40, //uint8_t 	bmRequestType,
                                6, //uint8_t 	bRequest,
                                0x100, //uint16_t 	wValue,
                                0,//uint16_t 	wIndex,
                                cmd,
                                CMD_DATALEN,
                                0	 
    );
    
    if(!(r == 0 &amp;amp;&amp;amp; actual_length &amp;gt;= CMD_DATALEN))
    {
        fprintf(stderr, &amp;quot;Write err %d. len=%d\n&amp;quot;,r,actual_length);
    }
    
    libusb_close(devh);
	libusb_free_device_list(devs, 1);
	libusb_exit(NULL);

    fprintf(stderr, &amp;quot;Done\n&amp;quot;);
	return 0;
}
&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sample usage:&lt;br /&gt;&lt;pre&gt;
./armedgetest 00 00 01
sleep 3
./armedgetest 00 00 00
&lt;/pre&gt;&lt;br /&gt;will turn the light on for 3 seconds and then would turn it off. It have been tested on MacOS X, but should work on Linux as well.</description>
  <comments>http://notbrainsurgery.livejournal.com/38622.html</comments>
  <category>api</category>
  <category>arm</category>
  <category>usb</category>
  <category>robotics</category>
  <category>robot</category>
  <category>owi</category>
  <category>edge</category>
  <category>libusb</category>
  <category>robotic</category>
  <lj:security>public</lj:security>
  <lj:reply-count>23</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/38215.html</guid>
  <pubDate>Fri, 29 Oct 2010 21:03:24 GMT</pubDate>
  <title>OWI-007 Robotic Arm parallel port protocol</title>
  <link>http://notbrainsurgery.livejournal.com/38215.html</link>
  <description>One of my projects involes controlling a robot arm. We were using &lt;a href=&quot;http://www.owirobot.com/products/Robotic-Arm-Trainer.html&quot; rel=&quot;nofollow&quot;&gt;Robotic Arm Trainer OWI-007&lt;/a&gt; from OWI. It is cheap (under $100). We were using their &lt;a href=&quot;http://www.robotshop.com/owi-arm-iterface-for-ibm.html&quot; rel=&quot;nofollow&quot;&gt;parallel port interface&lt;/a&gt;. OWI does not offer official API, but it was easy to figure out command sequences. Here are bytes one need to send to the parallel port, to control the arm:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;stop_arm		= 0x05,&lt;br /&gt;grip_open	= 0x0D,&lt;br /&gt;grip_close	= 0x00,&lt;br /&gt;elbow_up		= 0x15,&lt;br /&gt;elbow_down	= 0x01,&lt;br /&gt;wrist_cw		= 0x04,&lt;br /&gt;wrist_ccw		= 0x81,&lt;br /&gt;shoulder_up	= 0x03,&lt;br /&gt;shoulder_down = 0x45,&lt;br /&gt;base_ccw		= 0x02,&lt;br /&gt;base_cw		= 0x26&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Sending one of these commands will make an arm perform specified motion until STOP (0x05) or other command is sent. Only one motor at a time could be engaged.&lt;br /&gt;&lt;br /&gt;There is no speed control, but we have been able to implement a limited speed control using &lt;a href=&quot;http://en.wikipedia.org/wiki/Pulse-width_modulation&quot; rel=&quot;nofollow&quot;&gt;Pulse With Modulation&lt;/a&gt; approach.&lt;br /&gt;&lt;br /&gt;Now we are looking at their newer model, &lt;a href=&quot;http://www.owirobot.com/products/Robotic-Arm-Edge.html&quot; rel=&quot;nofollow&quot;&gt;Robotic Arm Edge&lt;/a&gt; which only comes with &lt;a href=&quot;http://www.owirobot.com/products/USB-Interface-for-Robotic-Arm-Edge.html&quot; rel=&quot;nofollow&quot;&gt;USB interface&lt;/a&gt;. OWI &lt;a href=&quot;http://www.facebook.com/permalink.php?story_fbid=165205690175594&amp;amp;id=111044195591744&quot; rel=&quot;nofollow&quot;&gt;&quot;don&apos;t offer any support for 3rd party programming for the USB interface&quot;&lt;/a&gt;, but I hope we can figure out their USB protocol on our own.</description>
  <comments>http://notbrainsurgery.livejournal.com/38215.html</comments>
  <category>owi</category>
  <category>robot</category>
  <category>api</category>
  <category>arm</category>
  <category>robotics</category>
  <category>owi-007</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/37970.html</guid>
  <pubDate>Wed, 06 Oct 2010 21:15:22 GMT</pubDate>
  <title>Google Apps</title>
  <link>http://notbrainsurgery.livejournal.com/37970.html</link>
  <description>My company has recently switched to Google Apps (paid edition). It is not cheap: $50 per user annually, but we decided it is worth it, comparing to cost of supporting our own email infrastructure. Google was kind enough to assign as a support engineer to help us with migration. We did migration on our own, and just needed help with few small issues. Before boring you with details let me go straight to conclusions, in case you do not want to read the rest:&lt;br /&gt;&lt;br /&gt;1. Core functionality of Google Apps works pretty well - we are happy&lt;br /&gt;2. Do not expect much support. My impression is that Google is doing whatever they think they should be doing, and do not care about you or your money. Just be grateful to be able to use their stuff and shut up. [This is similar to Apple&apos;s attitude, except Apple cares about your money] &lt;br /&gt;3. Knowing all this, would I still switch to Google Apps? Emphatically YES.&lt;br /&gt;&lt;br /&gt;Sample issues:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem:&lt;/b&gt; There is no way to browse global address book, although you can search in it.&lt;br /&gt;&lt;b&gt;Suggested Solution: &lt;/b&gt;&lt;br /&gt;	1. Export CVS and email to all your employees. Ask them to import to their private address books.&lt;br /&gt;	2. Use API (they actually offered to send me a link to API docs!) [I guess the assumption is that I should write my own interface to their address book]&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem:&lt;/b&gt; There is no way to change owner of documents on Google Docs from @gmail.com address to google apps user (it complains about different domains)&lt;br /&gt;&lt;b&gt;Suggested Solution:&lt;/b&gt; Export all documents in Ms Word/Excel formats and re-import them, losing history, sharing settings, etc.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem:&lt;/b&gt; Some problems with IMAP mailbox migration (Feature of apps control panel)&lt;br /&gt;&lt;b&gt;Suggested Solution:&lt;/b&gt;&lt;br /&gt;	1. Although it is offered via Control panel it is deprecated and should not be used. It is not supported.&lt;br /&gt;	2. Although I am not using Microsoft Exchange, I should install  &quot;Google Apps Migration for Microsoft Outlook&quot;. It will import IMAP as well. It requires Outlook to be installed. I will need Windows machine for this. Then it will proceed to download 4Gb of my mailboxes via IMAP and will re-upload them to Google Apps.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem:&lt;/b&gt; There was typo in name of one of users which we fixed. In the list of accounts it shows correctly, but in auto-completion in email and elsewhere it still shows mistyped one, one week after it was fixed.&lt;br /&gt;&lt;b&gt;Suggested Solution:&lt;/b&gt; Usually changes to user real names take 24 hours propagate. [When I pressed on saying that a week have passed, they said that this is known issue which will be fixed, eventually. No ETA, no workaround)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem:&lt;/b&gt; You can create calendar, and in sharing options make it shared with all people in the company. But co-workers have no way to find it - search does not show it.&lt;br /&gt;&lt;b&gt;Suggested Solution: &lt;/b&gt;You need to get cryptic calendar ID from sharing settings (it looks like email address) and email it to all your employees, and as them to add to their calendars using this ID.</description>
  <comments>http://notbrainsurgery.livejournal.com/37970.html</comments>
  <category>apps</category>
  <category>google</category>
  <lj:security>public</lj:security>
  <lj:reply-count>5</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/37859.html</guid>
  <pubDate>Fri, 23 Jul 2010 18:37:53 GMT</pubDate>
  <title>.DS_Store and SVN</title>
  <link>http://notbrainsurgery.livejournal.com/37859.html</link>
  <description>It is kind of obvous tip, but I want to share it anyway:&lt;br /&gt;&lt;br /&gt;I was annoyed by .DS_Store files left by Finder in my source directories. SVN reports them as unchecked. An easy way to fix it is to add .DS_Store to list of global igore patters. See &lt;i&gt;global-ignores&lt;/i&gt; in your &lt;i&gt;~/.svn/config&lt;/i&gt;. While at it, consider also adding &lt;i&gt;&apos;.pyc&apos;&lt;/i&gt; there is you are a python programmer ;)</description>
  <comments>http://notbrainsurgery.livejournal.com/37859.html</comments>
  <category>subversion</category>
  <category>svn</category>
  <category>ds_store</category>
  <category>macos</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/37417.html</guid>
  <pubDate>Mon, 14 Jun 2010 20:11:35 GMT</pubDate>
  <title>safari vs. chrome - user perspective</title>
  <link>http://notbrainsurgery.livejournal.com/37417.html</link>
  <description>I&apos;ve been oscillating between Safari and Google Chrome last few weeks without being able to decide which one I like better. Current status - I am back to Safari. Few reasons:&lt;br /&gt;&lt;br /&gt;1. PDF support - I often read PDF documents from the Web and Chrome method of using Google as PDF viewer is not good enough for me. I want real PDF viewer (Adobe or Preview) inside my browser window.&lt;br /&gt;&lt;br /&gt;2. Dictionary support: I like to be able to use Apple build-in dictionary/spelling tools in my browser. It does not work in Chrome. &lt;br /&gt;&lt;br /&gt;3. Email copy-paste: I am using internal Wiki for research and project notes. If I select some part of wiki page, copy and paste into email - in Safari I get formatting and embedded images. With Chrome I only get text.&lt;br /&gt;&lt;br /&gt;4. &lt;a href=&quot;http://agilewebsolutions.com/products/1Password&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;1Password&lt;/a&gt; plugin - Chrome version lacks many features&lt;br /&gt;&lt;br /&gt;5. Safari 5 &quot;Reader&quot; mode - looks like something I could use.&lt;br /&gt;&lt;br /&gt;6. Ability to configure handler for RSS URLs, so I can subscribe to RSS feeds in &lt;a href=&quot;http://www.newsgator.com/individuals/netnewswire/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;NetNewsWire&lt;/a&gt; from web page without copying and pasting URLs. &lt;br /&gt;&lt;br /&gt;In terms of speed and content rendering both browsers are looking pretty good, but small things like ones listed above are important for me.</description>
  <comments>http://notbrainsurgery.livejournal.com/37417.html</comments>
  <category>safari</category>
  <category>chrome</category>
  <category>mac</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/37234.html</guid>
  <pubDate>Thu, 15 Apr 2010 17:37:20 GMT</pubDate>
  <title>High-performance cloud servers</title>
  <link>http://notbrainsurgery.livejournal.com/37234.html</link>
  <description>For a project I am working on I need to use a lot of CPU power (training Neural Networks). Instead of buying fast server I decide to evaluate cloud-based solutions. For a long time Amazon Web Services user like myself &lt;a href=&quot;http://aws.amazon.com/ec2/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;EC2&lt;/a&gt; was the first choice. The fastest machine they offer is:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;a href=&quot;http://aws.amazon.com/ec2/instance-types/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Amazon High-CPU Extra Large Instance&lt;/a&gt;&lt;br /&gt;7 GB of memory&lt;br /&gt;20 EC2 Compute Units (8 virtual cores with 2.5 EC2 Compute Units each)&lt;br /&gt;1690 GB of instance storage&lt;br /&gt;64-bit platform&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The estimated price for non-stop usage during 30 days is $489.60.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.rackspacecloud.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Rackspace cloud&lt;/a&gt; do not offer high-CPU servers.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://gogrid.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;GoGrid&lt;/a&gt; offers&lt;i&gt; &quot;6 x Xeon&quot;&lt;/i&gt; as the fastest sever. The details vere scarce and I have contacted their support for clarification. They quoted me about $500 per month and gave $100 credit to try their service. I did. As you create an instance you could not implicitly choose CPU type. You can chose memory size and more memory you chose the faster CPU you will get. I had to call their support to find this out. So to get their fastest CPU you need to order 8Gb RAM machine.&lt;br /&gt;&lt;br /&gt;Then I have fired up two machines, one on EC2 and one on GoGrid and run a small (admitedly unscientific) test:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;EC2: &lt;/b&gt;&lt;br /&gt;&lt;i&gt;real &amp;#160; &amp;#160;717m21.988s&lt;br /&gt;user &amp;#160; &amp;#160;724m56.935s&lt;br /&gt;sys &amp;#160; &amp;#160; 0m45.715s&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;GoGrid:&lt;/b&gt;&lt;br /&gt;&lt;i&gt;real &amp;#160; &amp;#160;826m18.441s&lt;br /&gt;user &amp;#160; &amp;#160;809m10.078s&lt;br /&gt;sys &amp;#160; &amp;#160; 1m7.850s&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;So as we can see EC2 is roughly &lt;a href=&quot;http://www.wolframalpha.com/input/?i=%28809+minutes+10+seconds%29+%2F++%28724+minutes++56+seconds%29&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;1.1 times&lt;/a&gt; faster. Then I looked at their panel and discovered that they misquoted me their price. I checked again their web site. To run 8Gb instance for 30 days will cost me &lt;a href=&quot;http://www.wolframalpha.com/input/?i=30+days+in+hours+*+%241.52%2Fhr&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;$1094&lt;/a&gt;! Which is twice as much than EC2!&lt;br /&gt;&lt;br /&gt;So their unethical bait&amp;switch sales strategy aside, GoGrid is slightly slower and much more expensive than EC2.</description>
  <comments>http://notbrainsurgery.livejournal.com/37234.html</comments>
  <category>cloud</category>
  <category>amazon</category>
  <category>gogrid</category>
  <lj:security>public</lj:security>
  <lj:reply-count>2</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/37036.html</guid>
  <pubDate>Sat, 10 Apr 2010 18:53:18 GMT</pubDate>
  <title>iPad screen size</title>
  <link>http://notbrainsurgery.livejournal.com/37036.html</link>
  <description>To format some content for iPad screen, I was looking for exact screen dimensions. In official specs it states:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;9.7 inch diagonal&lt;br /&gt;&lt;li&gt;1024-by-768-pixel resolution at 132 pixels per inch (ppi)&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;From that we can deduct some useful numbers, such as:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;4:3 aspect ratio&lt;br /&gt;&lt;li&gt;7.76 by 5.82 inch&lt;br /&gt;&lt;li&gt;197.1 by 147.8 mm&lt;br /&gt;&lt;li&gt;5.197 pixels/mm&lt;br /&gt;&lt;/ul&gt;</description>
  <comments>http://notbrainsurgery.livejournal.com/37036.html</comments>
  <category>size</category>
  <category>ipad</category>
  <category>screen</category>
  <category>apple</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/36822.html</guid>
  <pubDate>Mon, 28 Dec 2009 00:37:54 GMT</pubDate>
  <title>unscientific google chrome performance test</title>
  <link>http://notbrainsurgery.livejournal.com/36822.html</link>
  <description>Google is advertising &lt;a href=&quot;http://www.google.com/chrome&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Chrome&lt;/a&gt; as a very fast alternative to other browsers. I am also trying to use &lt;a href=&quot;https://wave.google.com/wave/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Google Wave&lt;/a&gt; and I have noticed that on long waves, scrolling in Safari becomes a bit slow. So I decided to try Chrome. Surprisingly scrolling long wave on 1050x1680 (portrait mode) display on 2.8GHz MacBook Pro is much smoother and faster under Safari than under Chrome. In fact under Chrome scrolling is so jerky it is almost unusable.</description>
  <comments>http://notbrainsurgery.livejournal.com/36822.html</comments>
  <category>safari</category>
  <category>chrome</category>
  <category>test</category>
  <category>performance</category>
  <category>google</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/36421.html</guid>
  <pubDate>Fri, 18 Dec 2009 00:26:06 GMT</pubDate>
  <title>google app engine name cybersquatting</title>
  <link>http://notbrainsurgery.livejournal.com/36421.html</link>
  <description>While trying to come up with a name for a new application which will be hosted at &lt;a href=&quot;https://appengine.google.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt; google app engine&lt;/a&gt; I have realized that whatever name I can come up with is already taken. I suspect somebody wrote a bot to register all dicitionary words and their combinations. What would be any other explanation for names like &quot;&lt;i&gt;orangepotato&lt;/i&gt;&quot;, &quot;&lt;i&gt;fluffyrazor&lt;/i&gt;&quot; and &quot;&lt;i&gt;suicidesquirrel&lt;/i&gt;&quot; being already taken.</description>
  <comments>http://notbrainsurgery.livejournal.com/36421.html</comments>
  <category>app</category>
  <category>engine</category>
  <category>cybersquatting</category>
  <category>google</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/36252.html</guid>
  <pubDate>Wed, 05 Aug 2009 22:42:07 GMT</pubDate>
  <title>yubico and a false sense of security</title>
  <link>http://notbrainsurgery.livejournal.com/36252.html</link>
  <description>I was about to buy a &lt;a href=&quot;http://www.yubico.com/&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt; Yubico&lt;/a&gt; key today.  It is a security token which generates on time passwords (OTP) which could be used in conjuction with good old password to implement &lt;a href=&quot;http://en.wikipedia.org/wiki/Two-factor_authentication&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;two-factor authentication&lt;/a&gt;. There are many things I like about their product. The form factor is great. They use a very neat trick of simulating USB keyboard, which makes it very easy to integrate with OS without a need for a special drivers. The key already have been integrated with many applications as described on &lt;a href=&quot;http://wiki.yubico.com/wiki/index.php/Main_Page&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;their Wiki&lt;/a&gt;. They are also support open-source and try to be as open about their platform as possible.&lt;br /&gt;&lt;br /&gt;Before purchasing my yubico key I took my time too read their &lt;a href=&quot;http://www.yubico.com/files/YubiKey_manual-2.0.pdf&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;manual&lt;/a&gt; to understand better how their security works. The basic idea is very simple: each time you plug the key in and press a button it generates one time password. Yubico OTP is specially encoded AES encrypted block of data. The data consists of token id, random number, incrementing counter and some other fields. Let us take a look how it holds against some attacks:&lt;br /&gt;&lt;br /&gt;1. &lt;a href=&quot;http://en.wikipedia.org/wiki/Replay_attack&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Replay attack.&lt;/a&gt; Because the encrypted block includes incrementing counter, it seems to be safe from this kind of attack. Imagine we have sent OTP with counter=1. Malicious hacker, listening on the line have recorded it and tries to reuse it to log in again. However the server remembers last counter value and will not accept second authentication attempt unless counter value is greater than last used value for the token. So far so good. But here is the quote from their manual: &lt;i&gt;&quot;The non-volatile counter is compared with the previously received value. If lower  than or equal to the stored value, the received OTP is rejected as a replay.&quot;&lt;/i&gt; Quite sensibly they do not require next counter value to be exactly previous one plus one - any accidental button press when focus is in the wrong input field will break key/server synchronization. So if you try to use your key with two different servers, OTP generated for service A could be recorded and re-used for server B until user logged in to server B:&lt;br /&gt;&lt;br /&gt;1. User logs in to Service A with OTP-A1&lt;br /&gt;2. Hacker records OTP-A1&lt;br /&gt;3. Hacker logs in to Service B with OTP-A1&lt;br /&gt;4. User logs in to Service B with OTP-B1&lt;br /&gt;&lt;br /&gt;2. &lt;a href=&quot;http://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;Man-in-the-middle-attack&lt;/a&gt; - Imagine a piece of malicious software, pehaps in a form of a USB driver, which sits between the key and the server. User plugs in his key and presses a button for a first time. The driver records the OTP1 but either not sends it to the server, or sends a slightly modified (incorrect) version. The authentication fails. At this point user will likely just try again, this time emitting OTP2. The software will however will use OTP1 and will remember OTP2 for future use. At this point, at each login, the previous token will be used. The last token will be saved and can be used anytime later to gain unauthorized access on user behalf.&lt;br /&gt;&lt;br /&gt;3. &lt;u&gt;Server-side compromise&lt;/u&gt;. Imagine the server with which you use with your Yubico key is compromised. The hackers will gain access to AES key and token ID. This is all information they need now to simulate your token to generate one time passwords and try to log in to other systems set up to use the same token.&lt;br /&gt;&lt;br /&gt;In conclusion, first of all I must say that using Yubico in addition to password or some other authentication method is certainly an improvement in your security. But while doing so you should be aware that addition security provided by the key is not iron-clad and vulnerable to attacks listed above. The risks are slightly lower if you are not using same token with multiple services.</description>
  <comments>http://notbrainsurgery.livejournal.com/36252.html</comments>
  <category>security</category>
  <category>yubico</category>
  <lj:security>public</lj:security>
  <lj:reply-count>3</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://notbrainsurgery.livejournal.com/35925.html</guid>
  <pubDate>Wed, 08 Apr 2009 18:28:01 GMT</pubDate>
  <title>contemplative debugging</title>
  <link>http://notbrainsurgery.livejournal.com/35925.html</link>
  <description>After reading &lt;a href=&quot;http://asserttrue.blogspot.com/2009/04/turn-off-your-step-thru-debugger.html&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;this post&lt;/a&gt;, I want to share with you my take on the subject:&lt;br /&gt;&lt;br /&gt;More than a decade ago I was using &lt;a href=&quot;http://en.wikipedia.org/wiki/SGI_Indigo2&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;SGI Indigo II&lt;/a&gt; as my primary development workstation. We were writing in C++ and I have quickly discovered that SGI C++ compiler (&lt;a href=&quot;http://en.wikipedia.org/wiki/Cfront&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;cfront&lt;/a&gt; based) is pretty bad. To give you idea how bad it was - you can actually compile code which invokes pure virtual function, which, of course, was causing a crash during execution. So, we decided to switch to GCC, which was also not very mature back then in C++ department but was certainly a step ahead of SGI C++. The problem with G++ on SGI platform at that time was that for some reason it could not generate debug information in Irix executables. I think it has something to do with COFF executable format used by Irix 4.x, or something like that. So I had to  learn to live without debugger. I was relatively immature programmer without set work habits and I did not yet have preference to particular debugging technique. I worked like this for about a year, and then I switched to some other Unix platform after this, the &quot;damage&quot; have been done: I never had a need to use debugger ever since.&lt;br /&gt;&lt;br /&gt;This custom helped me several times during my career:&lt;br /&gt;&lt;br /&gt;1. Debugging PROLOG code where step-through debugging techniques are not applicable&lt;br /&gt;2. Programming mobile devices. (I remember we used to change a colour of a pixel in a upper right screen corner or emitting series of beeps as debugging techniques)&lt;br /&gt;3. Debugging Haskell programs (lazy evaluation, etc.)&lt;br /&gt;4. Debugging a distributed systems&lt;br /&gt;5. Diagnosing a problem reported by a customer, when you do not have access to actual device&lt;br /&gt;&lt;br /&gt;My friends and I were joking that we have invented a new debugging technique which we called &quot;&lt;i&gt;contemplative debugging&lt;/i&gt;&quot; (&quot;&lt;i&gt;созерцательная отладка&lt;/i&gt;&quot; in Russian). We used to practice a &lt;a href=&quot;http://en.wikipedia.org/wiki/Pair_programming&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;pair programming&lt;/a&gt;. It was before &quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Extreme_programming&quot; target=&quot;_new&quot; rel=&quot;nofollow&quot;&gt;extreme programming&lt;/a&gt;&quot; become poplular, and was mostly because we just do not have enough workstations. So, once the bug was reported, my partner and I intensively stared into suspect code until one of us emits cry of joy, discovering the problem. This technique, in many cases, turned out to be quite efficient, compared to firing up debugger and stepping through the code to get to the problem.&lt;br /&gt;&lt;br /&gt;P.S. I am not religious about not using debugger. I use it occasionally if need arise (which is not very often).</description>
  <comments>http://notbrainsurgery.livejournal.com/35925.html</comments>
  <category>debugging</category>
  <category>gcc</category>
  <category>irix</category>
  <category>sgi</category>
  <category>debugger</category>
  <category>c++</category>
  <lj:security>public</lj:security>
  <lj:reply-count>1</lj:reply-count>
</item>
</channel>
</rss>
