<?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-4073161</id><updated>2011-10-14T02:01:13.331+02:00</updated><category term='icfp'/><category term='kendo'/><category term='katana'/><category term='research'/><category term='computer vision'/><category term='motion capture'/><category term='refactoring'/><category term='python'/><category term='software development'/><title type='text'>Janto's blog</title><subtitle type='html'>verbose explanations of simple things, brief mentions of complex things.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://janto.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4073161.post-943218821305113295</id><published>2011-06-21T14:37:00.002+02:00</published><updated>2011-10-14T01:34:11.251+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><title type='text'>icfp 2011 contest</title><content type='html'>Our approach was more relaxed this year. I suspect we were more in the mood for fun hacking than doing well on the leader board.&lt;br /&gt;&lt;br /&gt;We played with the idea of using subversion instead of a &lt;a href="http://en.wikipedia.org/wiki/Distributed_revision_control"&gt;DVCS&lt;/a&gt;. I thought our workflow might require too many merges, but in the end &lt;a href="http://en.wikipedia.org/wiki/Mercurial"&gt;Mercurial&lt;/a&gt;'s branches worked well for us. It also made bringing old bots back to life pretty easy. Doing the same with subversion would probably have been more complicated.&lt;br /&gt;&lt;br /&gt;Our implementation was done in Python. Field values were either integers or objects with run methods (aliased with __call__). Functions like attack, heal and K that take more than one turn were implemented as nested class definitions. It was simple, and worked well. S was the most deeply nested:&lt;br /&gt;&lt;pre&gt;class S(Func):&lt;br /&gt;    def run(self, arg):&lt;br /&gt;&lt;br /&gt;        class S_partial1(Func):&lt;br /&gt;&lt;br /&gt;            def __repr__(next_self):&lt;br /&gt;                return "S(%s)"%arg&lt;br /&gt;&lt;br /&gt;            def run(next_self1, next_arg1):&lt;br /&gt;                class S_partial2(Func):&lt;br /&gt;                    def __repr__(next_self2):&lt;br /&gt;                        return "S(%s)(%s)"%(arg, next_arg1)&lt;br /&gt;                    def run(next_self2, next_arg2):&lt;br /&gt;                        return arg(next_arg2)(next_arg1(next_arg2))&lt;br /&gt;                return S_partial2()&lt;br /&gt;&lt;br /&gt;        return S_partial1()&lt;/pre&gt;&lt;br /&gt;We started with some very basic bots and worked our way up to more complex behavior. Many of our strategies are rather sensitive to interference as they where (initially) launched from the first few slots. If those slots are damaged by the opponent everything fails. Trying to capitalize on this we tried to monitor actions of opponent and attack slots that gets most "activity".&lt;br /&gt;&lt;br /&gt;Unfortunately the monitoring is rather complex and slow. So we experienced timeout issues on the test server.&lt;br /&gt;&lt;br /&gt;We also tried to detect how zombie cards are played and heal its target ASAP. Seems to work quite well and makes the opponent waste a few moves and messes with their state.&lt;br /&gt;&lt;br /&gt;Overall we think our strategies were sufficiently smart, but some of it just took too many turns and processing time.&lt;br /&gt;&lt;br /&gt;We wasted a few hours debugging a non-issue... I just forgot to call stdout.flush(). Also, in the end we might have submitted a buggy version. Oops! Will have to see how it goes. I should have made unit tests.&lt;br /&gt;&lt;br /&gt;This year's ICFP was a lot of fun! The organizers did not make any of the mistakes made in previous years: The problem was well defined, there wasn't any obfuscation for the sake of obfuscation, the barrier to entry was low and there weren't any serious issues with the test server. It was all done very well. From their blog comments they always seemed friendly and in good spirits. It's subtle things like this that I think make a competition fun.&lt;br /&gt;&lt;br /&gt;If there is any criticism then it's with the submission form. It probably made the organizers' job much easier, but it would have been nice to see the state of your own submission.&lt;br /&gt;&lt;br /&gt;Edit: We only bothered getting one camera working this year.&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="349" src="http://www.youtube.com/embed/1wtL9lDhvbg" width="425"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-943218821305113295?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/943218821305113295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=943218821305113295' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/943218821305113295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/943218821305113295'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2011/06/icfp-2011-contest.html' title='icfp 2011 contest'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/1wtL9lDhvbg/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-747335715233317951</id><published>2010-07-08T00:47:00.001+02:00</published><updated>2011-04-26T01:26:48.282+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><title type='text'>ICFP 2010 contest</title><content type='html'>Our team had 5 members this year: Myself, Loftie Ellis, Niel Thom, Pieter Holtzhausen and Albert Swart. It was Niel's first time participating and helped tremendously. We had a lot of fun. Mostly due to the team dynamics. The contest itself... not so much.&lt;br /&gt;&lt;br /&gt;We soon figured out the organisers tried to make things interesting by leaving out large parts of the spec. This might have been a good idea if there wasn't multiple plausible interpretations for the single provided example. And Occam's razor did not hold true.&lt;br /&gt;It took us about 24h to convince ourselves that our initial assumptions were incorrect. Our interpretation did not produce a consistent mapping. If it wasn't for a comment on the IRC channel, we might have wasted even more time trying to track down non-existent bugs.&lt;br /&gt;&lt;br /&gt;Overall I was disappointed in this year's problem. The barrier to entry was just too large. In fact, only about a quarter of registered teams received &lt;em&gt;any&lt;/em&gt; points. Meaning they either couldn't figure out the circuit format or couldn't compute the prefix. That's just silly. It should be easy for a team to get going, after which things can get as complex as they want.&lt;br /&gt;&lt;br /&gt;I doubt many teams got to the really interesting problems. If Loftie didn't submit the correct prefix 5am on Monday (about 7h before contest end), we wouldn't have had any points.&lt;br /&gt;&lt;br /&gt;I hope next year is organised by an American university. From my experience, their's are usually more fun.&lt;br /&gt;&lt;br /&gt;I again made some time lapse videos of us working. We played a lot of StarCraft towards the end :) I might put it on youtube some time.&lt;br /&gt;&lt;br /&gt;Update: Videos follow&lt;br /&gt;&lt;iframe title="YouTube video player" width="480" height="390" src="http://www.youtube.com/embed/RwbI6InAZ4k" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe title="YouTube video player" width="480" height="390" src="http://www.youtube.com/embed/RYSAP2Nqm7s" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-747335715233317951?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/747335715233317951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=747335715233317951' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/747335715233317951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/747335715233317951'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2010/07/icfp-2010-contest.html' title='ICFP 2010 contest'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/RwbI6InAZ4k/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-3498902855401788172</id><published>2009-06-29T23:27:00.000+02:00</published><updated>2010-04-14T00:00:10.303+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><title type='text'>icfp 2009 contest</title><content type='html'>Here's a quick technical writeup about our entry for this year's ICFP contest.&lt;br /&gt;&lt;br /&gt;Our team of 4 is called "&lt;span style="font-weight: bold;"&gt;xor eax, eax&lt;/span&gt;". i.e. myself, Loftie Ellis, Pieter Holtzhausen and Albert Swart.&lt;br /&gt;&lt;br /&gt;After a bit of discussion, Loftie and I set to work coding up the virtual machine specification in Python. We used the builtin &lt;a href="http://docs.python.org/library/array.html"&gt;array&lt;/a&gt; type to manage the bytecode, but later switched to numpy. Albert translated it to a C version, which Pieter hooked into Python code with ctypes. I wrote a simple GUI in &lt;a href="http://opencv.willowgarage.com/wiki/"&gt;opencv&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_8zeUfTUBeoA/Skop9_EK45I/AAAAAAAAAFI/5RQRaIztdPw/s1600-h/Screenshot-orbit-1.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5353137251960939410" src="http://2.bp.blogspot.com/_8zeUfTUBeoA/Skop9_EK45I/AAAAAAAAAFI/5RQRaIztdPw/s200/Screenshot-orbit-1.png" style="cursor: pointer; display: block; height: 200px; margin: 0px auto 10px; text-align: center; width: 194px;" /&gt;&lt;/a&gt;&lt;br /&gt;We encountered a few issues switching between our 32bit and 64bit machines. We had an 8x2GHz machine to our disposal, but ended up not really using it.&lt;br /&gt;&lt;br /&gt;Pieter wrote the basic Hohnmann transfer code, which Loftie used to manually determine target orbits for the first 2 problem sets. I used Python structs to write the submissions file.&lt;br /&gt;&lt;br /&gt;We finished it well within the lightning round time. In fact most of the work happened in the first 8 hours. At one stage we where fourth on the leader board. Unfortunately we spent too much time on the manual tweaking Saturday and was only able to do problem sets one, two and a single scenario of problem set 3 for the lightning round.&lt;br /&gt;&lt;br /&gt;We decided to approach problem 4 as a series of jumps between different orbital radii. In other words: to reach a target we need to do a Hohnmann transfer to a radius that will cause the target and our ship to meet. We did not have enough sleep and energy to explore the optimal sequential order of the jumps in too much detail.&lt;br /&gt;&lt;br /&gt;So Sunday I set to work on setting up scipy's optimization library to search for the optimal radius. We tended to primarily use the &lt;a href="http://en.wikipedia.org/wiki/Golden_section_search"&gt;Golden section search&lt;/a&gt;. That worked well and converged in around 35 iterations on average.&lt;br /&gt;&lt;br /&gt;Monday we didn't really do anything (except for playing a game of starcraft). The last update of the score board placed us at position 77. Since then we raised our score to around 2200, if I recall correctly, by primarily completing the remaining scenarios of problem 3.&lt;br /&gt;&lt;br /&gt;(&lt;b&gt;Edit: The final scores are in. We finished 70th out of 328 with a score of 2275.&lt;/b&gt;)&lt;br /&gt;&lt;br /&gt;We should have spent less time manually tweaking behavior and rather try to find a more general solution. We also should have looked at the scenario's in more detail, since we later discovered them to be simpler than the assumptions we were working with. I think these two factors sucked more of our energy than we should have allowed it to.&lt;br /&gt;&lt;br /&gt;I was hesitant when we began writing the VM in python, but it was surprisingly efficient. The C version was around 5x faster I would guess. Some of the other members were surprised by some python's scoping strangeness. It's interpreted nature allowed us a lot of experimentation, however. We used a local subversion repository, which worked out well.&lt;br /&gt;&lt;br /&gt;All things considered it was a good competition. The only major upset was a re-release of the problem 4 binary, which invalidated a few hours of our work. I have never really read about astrodynamics before this contest and found it very interesting.&lt;br /&gt;&lt;br /&gt;During the competition I continuously captured one image every minute from a webcam I mounted on the wall with sticky tape. Using mencoder I compiled it into a 25fps video. So each second in the video represents the passage of 25 minutes.&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/iJ_Hpf2g_vs&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/iJ_Hpf2g_vs&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-3498902855401788172?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/3498902855401788172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=3498902855401788172' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/3498902855401788172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/3498902855401788172'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2009/06/icfp-2009-contest.html' title='icfp 2009 contest'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_8zeUfTUBeoA/Skop9_EK45I/AAAAAAAAAFI/5RQRaIztdPw/s72-c/Screenshot-orbit-1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-7248762027267056345</id><published>2008-07-15T17:05:00.008+02:00</published><updated>2008-12-17T16:29:27.305+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>ICFP 2008 programming contest</title><content type='html'>It's been about a week since our team (myself, Pieter Holtzhausen, &lt;a href="http://www.loftie.com/2008/09/hello-world/"&gt;Loftie Ellis&lt;/a&gt; and Albert Swart) completed the &lt;a href="http://www.icfpcontest.org/"&gt;ICFP programming contest&lt;/a&gt;. I think I've had enough sleep now for a little writeup.&lt;br /&gt;&lt;br /&gt;The most important problem was that the provided sample maps and server was not necessarily representative of what we will be judged against. Assuming martians are not a threat (either not smart or too few), the optimal strategy is to ignore them, keep accelerating and dodge craters and boulders on the way to the home base. If a map contains martians of significant intelligence, they will have to be avoided.&lt;br /&gt;&lt;br /&gt;Our approach was to continuously switch between 2 modes:&lt;br /&gt;&lt;br /&gt;1. Given a goal position (initially the home base), move towards that goal. If there is an object in the path towards the goal, set the goal to be directly to the side of the obstacle. Once we arrive at a goal, reset the goal to the home base and repeat. &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_8zeUfTUBeoA/SIOJTwQxT-I/AAAAAAAAACc/Cjaqpu0Skpk/s1600-h/Screenshot-viewer-1.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_8zeUfTUBeoA/SIOJTwQxT-I/AAAAAAAAACc/Cjaqpu0Skpk/s200/Screenshot-viewer-1.png" alt="" id="BLOGGER_PHOTO_ID_5225170965145669602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. If our strategy is to take martians into account, predict if we will collide with a martian on our current velocity and path. If we will collide, change the goal position to be somewhere else.&lt;br /&gt;&lt;br /&gt;We decide on our mode based on what was learned about the map. By default we start in the first mode. If we were killed by a martian or have observed more than 4 martians at once in a previous run, start the next run in martian avoidance mode. If we ran into a crater, ignore the martians by next time starting in the first mode. &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_8zeUfTUBeoA/SIOJo99tkWI/AAAAAAAAACk/yxGYm8PI1es/s1600-h/Screenshot-viewer.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_8zeUfTUBeoA/SIOJo99tkWI/AAAAAAAAACk/yxGYm8PI1es/s200/Screenshot-viewer.png" alt="" id="BLOGGER_PHOTO_ID_5225171329601081698" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We wrote everything in Python. We spent most of the time on tweaking the goal following code, keeping the server communication stable and fighting with the lack of a Windows version of the server. Towards the end we tried A* path finding to hook into the goal following system, but decided not to include it in our final version. We also have an alternative visualization system based on OpenCV.&lt;br /&gt;&lt;br /&gt;I'm quite proud of our entry. We never collide with any craters or boulders if enemies are ignored. If enemies are taken into account we effectively make running away from them our short term mission. I'm a bit worried they will test our code against maps with dead ends, or that our martian avoidance code fails (because it hasn't been thoroughly tested).&lt;br /&gt;&lt;br /&gt;I predict the first few places will be taken by entries that can characterize the type of map quickly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-7248762027267056345?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/7248762027267056345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=7248762027267056345' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/7248762027267056345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/7248762027267056345'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2008/07/icfp-2008-programming-contest.html' title='ICFP 2008 programming contest'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_8zeUfTUBeoA/SIOJTwQxT-I/AAAAAAAAACc/Cjaqpu0Skpk/s72-c/Screenshot-viewer-1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-9083738202646642666</id><published>2007-03-30T07:42:00.001+02:00</published><updated>2008-09-01T15:31:51.447+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='kendo'/><category scheme='http://www.blogger.com/atom/ns#' term='katana'/><title type='text'>buying a katana</title><content type='html'>I've been bitten by the &lt;a href="http://en.wikipedia.org/wiki/Kendo"&gt;Kendo&lt;/a&gt; bug. I've been doing it for a few months now and I feel it's time I got myself a real blade.&lt;br /&gt;&lt;br /&gt;However, I don't want a wallhanger. I want something that can cut. Fortunately &lt;a href="http://www.chenessinc.com/"&gt;Cheness&lt;/a&gt; focuses on quality cutting-blades available for less than 300USD.&lt;br /&gt;&lt;br /&gt;I was really impressed by their &lt;a href="http://www.chenessinc.com/shura.htm"&gt;"Shura"&lt;/a&gt;. I've read a lot of really good reviews, especially the one at &lt;a href="http://www.sword-buyers-guide.com/tenchi.html"&gt;sword-buyers-guide&lt;/a&gt; and the destruction test at &lt;a href="http://www.rsknives.co.uk/review.html"&gt;RS knives&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Living in South Africa, I knew there would be some problems getting it shipped here. So I decided to purchase it through Paul from &lt;a href="http://www.sword-buyers-guide.com/"&gt;sword-buyers-guide&lt;/a&gt;. I am very thankful to Paul for going out of his way to help me. His customer support is excellent.&lt;br /&gt;&lt;br /&gt;The sword is now on its way. Hopefully it will get here safely.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-9083738202646642666?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/9083738202646642666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=9083738202646642666' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/9083738202646642666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/9083738202646642666'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2007/03/buying-katana.html' title='buying a katana'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-3442468913236057818</id><published>2006-12-03T01:32:00.001+02:00</published><updated>2011-02-01T21:02:37.283+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='research'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>interactive recognition of hand-drawn circuit diagrams</title><content type='html'>After six years of study I completed my degree of BEng/MScEng (with computer science) last week. Yay! Here is a video of the final prototype:&lt;br /&gt;&lt;br /&gt;&lt;embed style="width:400px; height:326px;" id="VideoPlayback" type="application/x-shockwave-flash" src="http://video.google.com/googleplayer.swf?docId=905906717163261495&amp;hl=en" flashvars=""&gt; &lt;/embed&gt;&lt;br /&gt;&lt;br /&gt;You can download my full &lt;a href="http://jantod.googlepages.com/circuit_sketch.pdf"&gt;thesis&lt;/a&gt; or some &lt;a href="http://jantod.googlepages.com/circuit_sketch_presentation.pdf"&gt;slides&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Edit 2011/02/01&lt;/b&gt;: Here is the &lt;a href="https://bitbucket.org/janto/circuit_sketch"&gt;source code&lt;/a&gt;. It's a bit of a mess and it's going take a bit of work to get it going on newer systems because it probably requires an older wxWindows. The interesting parts are in &lt;i&gt;src/linetools.py&lt;/i&gt;. If you find it useful or reference my work I'd love to hear about it.&lt;br /&gt;&lt;br /&gt;One of my favourite opinionated parts:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;It is generally believed that state coverage is superior to code coverage. It is the author's opinion, however, that state coverage is less important in Python than other languages. As Python typically processes collections (such as lists) using an iterator and not incrementing indices, there are less ways in which errors can occur. Since variables also need not be declared it is also less likely that &lt;em&gt;null&lt;/em&gt; pointers would be dereferenced. This is, for example, one of the most frequent sources of errors in the Java programming language. &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-3442468913236057818?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/3442468913236057818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=3442468913236057818' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/3442468913236057818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/3442468913236057818'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/12/interactive-recognition-of-hand-drawn.html' title='interactive recognition of hand-drawn circuit diagrams'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-115377471292414818</id><published>2006-07-24T22:27:00.001+02:00</published><updated>2008-09-01T15:32:59.450+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>ICFP contest done</title><content type='html'>My team and I participated in this year's &lt;a href="http://icfpcontest.org/"&gt;ICFP programming contest&lt;/a&gt; - a weekend of very little sleep and lots of programming. This was the first year our team (i.e. &lt;a href="http://www.youtube.com/watch?v=ADQ78Y1icaM&amp;mode=related&amp;search=monty%20python"&gt;The Black Knight Always Triumphs&lt;/a&gt;) took part.&lt;br /&gt;&lt;br /&gt;We were amazed at the effort the judges must have gone through to prepare the problems. My favorite problems where writing the virtual machine that ran the problems and hacking into "user accounts" using their stripped down version of Basic which uses Roman numerals for line numbers.&lt;br /&gt;&lt;br /&gt;We expect to finish under the first third of participants (that made it to the scoreboard) and we'll definately be back next year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-115377471292414818?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/115377471292414818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=115377471292414818' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115377471292414818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115377471292414818'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/07/icfp-contest-done.html' title='ICFP contest done'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-115271540265288451</id><published>2006-07-12T16:11:00.001+02:00</published><updated>2008-09-01T15:33:06.509+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>del.icio.us importing</title><content type='html'>I wanted to change my &lt;a href="http://del.icio.us/"&gt;del.icio.us&lt;/a&gt; username. You aren't allowed to do this directly, but you can export your links to an html file and try to import it back into a new account. &lt;strike&gt;Unfortunately their import function can't parse the data generated by their export function. Strange.&lt;/strike&gt; (apparently fixed now) Luckely they have an open &lt;a href="http://del.icio.us/help/api/"&gt;API&lt;/a&gt; (and a whole bunch of &lt;a href="http://del.icio.us/help/thirdpartytools"&gt;third-party tools&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;So with the help of &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; I simply parsed the exported html and used &lt;a href="http://deliciouspython.python-hosting.com/"&gt;pydelicious&lt;/a&gt; to post it back into delicious. Like So:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from BeautifulSoup import BeautifulSoup&lt;br /&gt;import pydelicious&lt;br /&gt;from itertools import count&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;  soup = BeautifulSoup(file("export.htm").read())&lt;br /&gt;  user = "user"&lt;br /&gt;  password = "pass"&lt;br /&gt;  items = soup.findAll("a")&lt;br /&gt;  print len(items)&lt;br /&gt;  for item, n in zip(items, count()):&lt;br /&gt;    href = item["href"]&lt;br /&gt;    tags = item["tags"]&lt;br /&gt;    title = item.string&lt;br /&gt;    add_date = item["add_date"]&lt;br /&gt;    last_modified = item["last_modified"]&lt;br /&gt;    print n,&lt;br /&gt;    try:&lt;br /&gt;      pydelicious.add(user, password, href, title, tags=tags, dt=add_date, replace="yes")&lt;br /&gt;    except Exception, e:&lt;br /&gt;      print e&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ah, I love Python.&lt;br /&gt;&lt;br /&gt;While writing this post I ran into some &lt;a href="http://www.hackdiary.com/archives/000060.html"&gt;del.icio.us experiments&lt;/a&gt;, which might be useful to someone. And a &lt;a href="http://www.hackdiary.com/archives/000061.html"&gt;follow-up&lt;/a&gt; with very pretty graphs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-115271540265288451?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/115271540265288451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=115271540265288451' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115271540265288451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115271540265288451'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/07/delicious-importing.html' title='del.icio.us importing'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-115116199721712584</id><published>2006-06-24T17:02:00.000+02:00</published><updated>2006-06-24T17:13:17.233+02:00</updated><title type='text'>I want; I need</title><content type='html'>Feature requests I filed in last half hour. Not all of them are really thought through that well, but I just want to be sure the developers are aware of my needs.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.7-zip.org/"&gt;7-zip&lt;/a&gt; (compression):&lt;ul&gt;&lt;li&gt;&lt;a href="https://sourceforge.net/forum/forum.php?thread_id=1524649&amp;forum_id=45797"&gt;checkout/install files&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://sourceforge.net/forum/forum.php?thread_id=1524653&amp;forum_id=45797"&gt;always extract to subfolder&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flock.com/"&gt;Flock&lt;/a&gt; (webbrowser):&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.flock.com/forums/suggestions/customize-search-engines-from-options"&gt;customize search engines from options&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.flock.com/forums/suggestions/page-links-active-waiting-for-load"&gt;links on page work between link is followed and one is activated&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.jhorman.org/wikidPad/"&gt;wikidPad&lt;/a&gt; (note keeper):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://groups.yahoo.com/group/wikidPad/message/2198"&gt;scan icons subdirectory and image pasting also copies image to a subdir&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-115116199721712584?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/115116199721712584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=115116199721712584' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115116199721712584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/115116199721712584'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/06/i-want-i-need.html' title='I want; I need'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-114823358327524417</id><published>2006-05-21T19:33:00.001+02:00</published><updated>2008-09-01T15:33:24.095+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>os.listdir iterator</title><content type='html'>I found an &lt;a href="http://mail.python.org/pipermail/python-list/2003-January/141790.html"&gt;iterator version&lt;/a&gt; of os.listdir for POSIX(?) systems, but I needed one for Windows. Specifically to check if a directory has subdirectories.&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;import win32file&lt;br /&gt;import pywintypes&lt;br /&gt;def has_subdirectories(path):&lt;br /&gt;  try:&lt;br /&gt;    for info in win32file.FindFilesIterator(os.path.join(path, "*")):&lt;br /&gt;      if info[0] &amp; win32file.FILE_ATTRIBUTE_DIRECTORY and info[-2] not in [".", ".."]:&lt;br /&gt;        return True&lt;br /&gt;  except pywintypes.error: # access denied&lt;br /&gt;    pass&lt;br /&gt;  return False&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-114823358327524417?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/114823358327524417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=114823358327524417' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114823358327524417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114823358327524417'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/05/oslistdir-iterator.html' title='os.listdir iterator'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-114677817732668243</id><published>2006-05-04T23:28:00.001+02:00</published><updated>2008-09-01T15:34:11.114+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><title type='text'>Characterizing People as Non-Linear, First-Order Components in Software Development</title><content type='html'>Alistair Cockburn has written a lot of very interesting &lt;a href="http://alistair.cockburn.us/crystal/articles/alistairsarticles.htm"&gt;articles&lt;/a&gt; but I have to agree with John Carter on &lt;a href="http://lambda-the-ultimate.org/"&gt;Lambda the Ultimate&lt;/a&gt; when he called &lt;a href="http://alistair.cockburn.us/crystal/articles/cpanfocisd/characterizingpeopleasnonlinear.html"&gt;this one&lt;/a&gt; his "absolute favourite paper in all the CS literature".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-114677817732668243?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://alistair.cockburn.us/crystal/articles/cpanfocisd/characterizingpeopleasnonlinear.html' title='Characterizing People as Non-Linear, First-Order Components in Software Development'/><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/114677817732668243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=114677817732668243' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114677817732668243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114677817732668243'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/05/characterizing-people-as-non-linear.html' title='Characterizing People as Non-Linear, First-Order Components in Software Development'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-114375597830127133</id><published>2006-03-30T23:53:00.001+02:00</published><updated>2008-09-01T15:34:29.285+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='icfp'/><title type='text'>icfp contest date announced</title><content type='html'>It's clear what I'll be doing the weekend of July 21–24. The annual ICFP programming contest is &lt;em&gt;the&lt;/em&gt; programming contest. This year I have to enter. Even if my solution doesn't run.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-114375597830127133?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://icfpcontest.org/' title='icfp contest date announced'/><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/114375597830127133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=114375597830127133' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114375597830127133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114375597830127133'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/03/icfp-contest-date-announced.html' title='icfp contest date announced'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-114078042781688742</id><published>2006-02-24T13:04:00.001+02:00</published><updated>2009-11-22T00:17:09.776+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><title type='text'>scite &amp; bicycle repair man</title><content type='html'>&lt;a href="http://www.scintilla.org/SciTE.html"&gt;SciTE&lt;/a&gt; has been my favorite editor for a long time now. Especially for Python code. But there are two things I really miss: refactoring capabilities and a graphical code overview.&lt;br /&gt;&lt;br /&gt;A few years ago I integrated refactoring using &lt;a href="http://bicyclerepair.sourceforge.net/"&gt;Bicycle Repair Man&lt;/a&gt;. That code has been lost but yesterday I decided to quickly rewrite it. It &lt;strike&gt;won't work with files using tabs for indentation&lt;/strike&gt; (fixed with hack) due to an irritating bug in SciTE (which still &lt;a href="https://sourceforge.net/tracker/?func=detail&amp;amp;aid=760453&amp;amp;group_id=2439&amp;amp;atid=102439"&gt;hasn't been fixed&lt;/a&gt; since I filed it in 2003, unfortunately). Comments and suggestions are welcome!&lt;br /&gt;&lt;br /&gt;Download it here: &lt;a href="http://bitbucket.org/janto/snippets/src/tip/scitebike.py"&gt;http://bitbucket.org/janto/snippets/src/tip/scitebike.py&lt;/a&gt;&lt;br /&gt;See the file for installation instructions.&lt;br /&gt;&lt;br /&gt;Now about that graphical code overview...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-114078042781688742?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/114078042781688742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=114078042781688742' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114078042781688742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/114078042781688742'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/02/scite-bicycle-repair-man.html' title='scite &amp; bicycle repair man'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4073161.post-113725065329390083</id><published>2006-01-14T16:54:00.001+02:00</published><updated>2009-09-02T01:33:02.597+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer vision'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='motion capture'/><title type='text'>motion capture in python</title><content type='html'>You know those suits with the ping-pong balls attached to it? The kind they use in &lt;a href="http://en.wikipedia.org/wiki/Motion_capture"&gt;motion capture&lt;/a&gt;? I thought it would be really cool if I could build my own. Then I'd be able to film my kickboxing and analyze it afterwards.&lt;br /&gt;&lt;br /&gt;The current state-of-the-art seems to be tracking a magnetic field with the sensors on the suit. Unfortunately I don't have the resources to do that. So I'm going with the older way: visually tracking the markers.&lt;br /&gt;&lt;br /&gt;I capture frames using my cheap webcam and the &lt;a href="http://videocapture.sourceforge.net/"&gt;VideoCapture&lt;/a&gt; Python library. Others have used it for simple &lt;a href="http://gumuz.looze.net/wordpress/index.php/archives/2005/06/06/python-webcam-fun-motion-detection/trackback/"&gt;motion detection&lt;/a&gt; but not motion &lt;i&gt;tracking&lt;/i&gt;. I decided to track the motion of red objects. Why red? Well, it's one of the least common colours in my office and is usually concentrated to a small area (bottle caps, mugs, books).&lt;br /&gt;&lt;br /&gt;To get my hands on the red pixels I can't simply extract the R from RGB images. If a colour has a red component it could also be white, yellow, magenta... So you take the R and subtract the G and B. Ignoring values below zero you now have a gauge for the "redness" of a pixel.&lt;br /&gt;&lt;br /&gt;(Extra points: Correct me if I'm wrong, but I believe "R minus G minus B" will give you a "redness" plane that intersects the xy chromaticity diagram (see &lt;a href="http://en.wikipedia.org/wiki/SRGB_color_space"&gt;sRGB color space&lt;/a&gt;) as a line running from blue to green along the edge of the sRGB gamut. The triangular plane segment within the gamut reaches it's "peak" at the maximum red representable by sRGB.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/282/134/1600/ducky_crosshair.jpg"&gt;&lt;img alt="" border="0" src="http://photos1.blogger.com/blogger/282/134/320/ducky_crosshair.jpg" style="float: right; margin: 0px 0px 10px 10px;" /&gt;&lt;/a&gt;This is a simple way to find the maximum red point on the image. I drew a crosshair over that point for clarity. With the series of images captured by the webcam I constructed an animated gif. So the system tracks a red object. Cool.&lt;br /&gt;&lt;br /&gt;However, what I want to do is track more than one red object. I did this by forming clusters of red dots above certain intensity.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/282/134/1600/ducky_and_cokes.jpg"&gt;&lt;img alt="" border="0" src="http://photos1.blogger.com/blogger/282/134/320/ducky_and_cokes.jpg" style="cursor: pointer; float: right; margin: 0pt 0pt 10px 10px;" /&gt;&lt;/a&gt;How do you form clusters? Well I used the &lt;a href="http://en.wikipedia.org/wiki/K-means_algorithm"&gt;K-means algorithm&lt;/a&gt;, conveniently included with &lt;a href="http://www.scipy.org/"&gt;SciPy&lt;/a&gt;. Et viola! It seems to do a pretty good job of it.&lt;br /&gt;&lt;br /&gt;Not bad for a days work. Only 150 lines of Python. Of course it is at this point that my fear is confirmed: My webcam doesn't seem up to the task of tracking the speed of my movements. So before I jump around with a bunch of red markers on my body, I'll need a better camera.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Edit&lt;/b&gt;: You can download a rough version of the code here: &lt;a href="http://jantod.googlepages.com/motion_capture.zip"&gt;motion_capture.zip&lt;/a&gt;. Parts are commented out so you don't need a webcam. You will need Python, SciPy and &lt;a href="http://www.pythonware.com/products/pil/"&gt;PIL&lt;/a&gt; however. I hope this works...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Another Edit&lt;/b&gt;: Here's an alternative I wrote (&lt;a href="http://jantod.googlepages.com/vr_tracker2.zip"&gt;vr_tracker2.zip&lt;/a&gt;) that uses numarray, &lt;a href="http://bonsai.ims.u-tokyo.ac.jp/%7Emdehoon/software/cluster/software.htm#pycluster"&gt;Pycluster&lt;/a&gt; and PIL instead. I'm starting to dislike SciPy. If there are enough requests I think I'll have to do another write-up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4073161-113725065329390083?l=janto.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://janto.blogspot.com/feeds/113725065329390083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4073161&amp;postID=113725065329390083' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/113725065329390083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4073161/posts/default/113725065329390083'/><link rel='alternate' type='text/html' href='http://janto.blogspot.com/2006/01/motion-capture-in-python.html' title='motion capture in python'/><author><name>Janto Dreijer</name><uri>http://www.blogger.com/profile/13998975623132642083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry></feed>
