<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="http://www.richwareham.com//feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.richwareham.com//" rel="alternate" type="text/html" /><updated>2022-11-10T10:45:50+00:00</updated><id>http://www.richwareham.com//feed.xml</id><title type="html">Rich Wareham</title><subtitle>:ramen: Minimalist Jekyll Template</subtitle><entry><title type="html">Getting Started with Electronics</title><link href="http://www.richwareham.com//getting-started-in-electronics/" rel="alternate" type="text/html" title="Getting Started with Electronics" /><published>2015-03-03T00:00:00+00:00</published><updated>2015-03-03T00:00:00+00:00</updated><id>http://www.richwareham.com//getting-started-in-electronics</id><content type="html" xml:base="http://www.richwareham.com//getting-started-in-electronics/">&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;Towards the end of 2014 I decided that I wanted to learn digital electronics.
As someone who would self-identify as a software person I only had a relatively
wooley understanding of what happened at the “sand layer” of my computer. I’ve
set myself the project of designing and making a 1980s-era computer called
“Búri”. I’ve recorded a few YouTube videos on it:&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/videoseries?list=PLQdwYJ5AuTolZuQIYEujwqP3wV1qRbEa8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;This post covers the “bootstrapping” process I went through to get myself to a
place where I could start learning electronics. Its the set of things I wish I
knew when I started.&lt;/p&gt;

&lt;h1 id=&quot;the-essentials&quot;&gt;The essentials&lt;/h1&gt;

&lt;p&gt;Unlike software, electronics is fundamentally concerned with individual
&lt;em&gt;things&lt;/em&gt;. And the thing about &lt;em&gt;things&lt;/em&gt; is that the incremental cost of making
and delivering an individual &lt;em&gt;thing&lt;/em&gt; is far greater than software. (Making a
copy of a piece of software is very nearly free.) Hence you’ll need to spend
some money. I spent quite a bit of money but if I were to bootstrap again, this
is what I’d buy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;£5.48 - &lt;a href=&quot;http://www.ebay.co.uk/itm/UNO-R3-ATmega328P-CH340T-USB-Arduino-Compatible-Microcontroller-Board-UK-STOCK-/181678624617&quot;&gt;Arduino UNO-alike&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;£3.73 - &lt;a href=&quot;http://www.ebay.co.uk/itm/MB-102-830-Point-Solderless-PCB-Breadboard-Jump-Cable-Wires-65pcs-Power-Supply-/360736049270&quot;&gt;Solderless breadboard, wires and power supply&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;£16.74 - &lt;a href=&quot;http://www.bitsbox.co.uk/index.php?main_page=product_info&amp;amp;cPath=272_276&amp;amp;products_id=2009&quot;&gt;Electronics component starter kit&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prices inclusive of postage and correct at time of writing. Total cost: £25.95
or, again as of writing, around $45 (US). Note that I’m not endorsing these
individual eBay sellers; YMMV when ordering from China.  I was also lucky in
that my Dad, on hearing of my new hobby, found a large box of chips, resistors,
capacitors, etc. in the garage. Most of my projects have now been driven more
by what I have for free in those boxes rather than what I’ve bought.&lt;/p&gt;

&lt;p&gt;You can never have enough solderless breadboards. I bought a A4-sized
whiteboard from Poundland and stuck breadboards to it. This is a lot cheaper
than buying one of the metal-backed “advanced” breadboard modules. You can see
it in action in the videos above and, in an earlier state, below:&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/lmBjuM0IS_4&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;em&gt;(The video itself is the first test of using a
&lt;a href=&quot;http://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf&quot;&gt;MAX7219&lt;/a&gt; chip
to drive a set of seven segment displays from the Big Box. This ended up
forming the core of the debug board for my homebrew 6502 computer.)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;less-essential-essentials&quot;&gt;Less essential essentials&lt;/h2&gt;

&lt;p&gt;Aside from discrete components like resistors, capacitors and LEDs, if you want
to play with digital electronics like I did you’ll need some
&lt;a href=&quot;http://en.wikipedia.org/wiki/List_of_7400_series_integrated_circuits&quot;&gt;7400 series logic&lt;/a&gt;.
I inherited a lot from my Dad in
the Big Box O’ Joy. That being said, the chips you’ll definitely want a handful
of are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;74HC00 - NAND gates.&lt;/li&gt;
  &lt;li&gt;74HC02 - NOR gates.&lt;/li&gt;
  &lt;li&gt;74HC04 - NOT gates.&lt;/li&gt;
  &lt;li&gt;74HC74 - D-type flip-flops.&lt;/li&gt;
  &lt;li&gt;74HC165 - 8-bit “output” shift register.&lt;/li&gt;
  &lt;li&gt;74HC595 - 8-bit “input” shift register.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter two chips are useful if you’re using an Arduino to drive lots of
outputs or getting lots of inputs. You’ll be surprised at how small “lots” can be.&lt;/p&gt;

&lt;p&gt;There’s a good
&lt;a href=&quot;https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA&quot;&gt;Computerphile&lt;/a&gt; video
on the use of flip-flops in computer memory if you don’t know your “D-type”s
from your “JK-type”s.&lt;/p&gt;

&lt;h1 id=&quot;knowledge&quot;&gt;Knowledge&lt;/h1&gt;

&lt;p&gt;Once you’ve got your equipment, you need to start learning.  I started with a
little basic electronics knowledge from school and a couple of courses at
University.  I was rather dismissive of electronics ad University viewing it as
a subject deeply steeped in “magic”. As I’ve gotten older, I’ve realised that
electronics people viewed software as similarly being the domain of wizards and
sorcery. Such is any skill when viewed from outside.&lt;/p&gt;

&lt;p&gt;That being said I was aware, perhaps distantly, of the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Voltage and current are different like “pressure” and “flow” are different.
Current is the flow of electrons and voltage is how much those electrons
want to flow.&lt;/li&gt;
  &lt;li&gt;Capacitors let &lt;em&gt;alternating current&lt;/em&gt; pass but block &lt;em&gt;direct current&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Diodes let current flow in one direction only.&lt;/li&gt;
  &lt;li&gt;Transistors act like an electrically controlled switch although I was more
than a little fuzzy on the difference between NPN, PNP, bipolar junction,
MOSFET, etc, etc.&lt;/li&gt;
  &lt;li&gt;Resistors obey &lt;a href=&quot;http://en.wikipedia.org/wiki/Ohm%27s_law&quot;&gt;Ohm’s law&lt;/a&gt;.
(Otherwise known as the &lt;strong&gt;V&lt;/strong&gt;ery &lt;strong&gt;I&lt;/strong&gt;mportant &lt;strong&gt;R&lt;/strong&gt;ule.)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws&quot;&gt;Kirchhoff’s circuit
laws&lt;/a&gt;. (Otherwise
known as “current and voltage don’t appear or disappear by magic”.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of these, the first is the most important. If you’re not sure of the difference
between voltage and current you’re going to have a bad time and you should
watch some YouTube videos to get a good handle on the difference.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aside: more correctly, I was aware that current is the flow of the places
where electrons want to be. &lt;a href=&quot;http://en.wikipedia.org/wiki/Electric_current#Current&quot;&gt;Conventional
positive-to-negative&lt;/a&gt;
current flow predates the discovery that electrons are negatively charged!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had, therefore, something of a schoolboy electronics knowledge with some
pretty large gaps but YouTube provided some important gap-filling mortar. I
can’t stress how useful being able to sit down with a cup of tea and watch the
odd ten minutes of electronics tutorial videos was.&lt;/p&gt;

&lt;h2 id=&quot;a-first-project&quot;&gt;A first project&lt;/h2&gt;

&lt;p&gt;The first circuit that almost anyone does these days is the Arduino “blinking
LED” circuit. This consists of an LED, a resistor and an Arduino. There’s not
much to that but I like to try and learn a little bit of theory from everything
I do. The first question which popped into my head was “why do we need the
resistor?” I’ll let the interested reader Google for better explanations than I
could write here but suffice it to say that I found it pleasing to be able to
calculate what value that resistor should have. Even the simplest of circuits
can be a learning opportunity.&lt;/p&gt;

&lt;p&gt;From deep in my old bedroom in my parents’ house came the book
&lt;a href=&quot;http://www.amazon.co.uk/Adventures-Microelectronics-Tom-Duncan/dp/0719536715&quot;&gt;Adventures with Microelectronics&lt;/a&gt;.
I’m sure many similar books exist which were printed &lt;em&gt;after&lt;/em&gt; the colours brown
and yellow went out of fashion. This book has a few simple circuits which can
be built on a breadboard and which cover the basics of wiring things up,
designing oscillators, etc. If you could find an introductory e-book on
something Arduino related, that’d probably be just as good. Working through the
book was the work of a single day but was fun to “get my feet wet”.&lt;/p&gt;

&lt;h2 id=&quot;a-stretch-project&quot;&gt;A stretch project&lt;/h2&gt;

&lt;p&gt;I found it important to set a “stretch goal”: a project which seemed imposing
and difficult to begin with but could be broken down into stages which would
spark off simple projects along the way. As outlined in the introuction, I
chose to implement a 1980s-style microcomputer. The thought was that if I can
get to the point where I can compile C, I had “closed the gap” in my knowledge
between chip and code.&lt;/p&gt;

&lt;h1 id=&quot;youtube-let-a-thousand-lectures-bloom&quot;&gt;YouTube: let a thousand lectures bloom&lt;/h1&gt;

&lt;p&gt;YouTube is an amazing resource for learning while in the bath. Queue up some
videos, draw some suds and lie and relax. If you;re learning electronics,
searching for “arduino” will consume many happy hours. If you want some
specific recommendations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA&quot;&gt;Computerphile&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/user/julius256&quot;&gt;Julian Ilett&lt;/a&gt;. His “postbag”
segment lets you in to all the fun little electronics modules you can get
from eBay.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLvOlSehNtuHtWlH0UOZNtOn-FlFCn1GYw&quot;&gt;EEVblog “Fundamentals Friday”&lt;/a&gt;
YouTube playlist.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/user/thebenheckshow&quot;&gt;The Ben Heck Show&lt;/a&gt;. This is
more for inspiration than eductaion but is still fun.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h1&gt;

&lt;p&gt;This post contains some of what I wished I knew when starting out in
electronics. Specifically, it gives an initial, affordable, shopping list. I
hope you find it useful!&lt;/p&gt;</content><author><name></name></author><summary type="html">Introduction</summary></entry><entry><title type="html">Fonts from old ROMs with Python</title><link href="http://www.richwareham.com//font-from-bbc-rom/" rel="alternate" type="text/html" title="Fonts from old ROMs with Python" /><published>2015-02-03T00:00:00+00:00</published><updated>2015-02-03T00:00:00+00:00</updated><id>http://www.richwareham.com//font-from-bbc-rom</id><content type="html" xml:base="http://www.richwareham.com//font-from-bbc-rom/">&lt;p class=&quot;preamble&quot;&gt;This post is also available as an IPython notebook which may be
&lt;a href=&quot;/downloads/2015-02-03-font-from-bbc-rom.ipynb&quot;&gt;downloaded&lt;/a&gt;
or &lt;a href=&quot;/downloads/2015-02-03-font-from-bbc-rom.ipynb&quot;&gt;viewed online&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;For a personal project recently I needed a simple monospaced 8x8 pixel font. I
could, of course, make my own but I thought it’d be nice for nostalgia reasons
to try and extract the Acorn 8x8 font used in the old &lt;a href=&quot;http://en.wikipedia.org/wiki/BBC_Micro&quot;&gt;BBC
Microcomputer&lt;/a&gt;. Using Python we can
directly extract the font from a zipfile containing a ROM image which we
download from the web. We can then use the &lt;a href=&quot;https://imgur.com&quot;&gt;imgur&lt;/a&gt; API to
upload the resulting font directly to the web.&lt;/p&gt;

&lt;h1 id=&quot;implementation&quot;&gt;Implementation&lt;/h1&gt;

&lt;p&gt;You can find a copy of the &lt;a href=&quot;http://www.8bs.com/mag/32/bbcmemmap2.txt&quot;&gt;BBC Model B memory
map&lt;/a&gt; online. The relevant section for
our needs is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PAGE 192 (&amp;amp;C0) to 194 (&amp;amp;C2) : OS ROM

C000-C2FF Character font lookup table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This tells us that the character font table is in the first 0x300 (or 768) bytes
of the OS ROM. The OS ROMs are available from multiple sources but there is a
&lt;a href=&quot;http://www.bbcmicrogames.com/roms.html&quot;&gt;page&lt;/a&gt; on a BBC Micro games site which
has a link.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;OS_ROM_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'http://www.bbcmicrogames.com/roms/os12.zip'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a zip file and so we can use the Python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zipfile&lt;/code&gt; module to examine
the file after downloading it into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bytes&lt;/code&gt; object via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt;
library:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;# downloading the zipfile
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BytesIO&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# treating a bytes object as file
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;zipfile&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;# parsing the zipfile
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Fetch the ROM and check that the GET succeeded
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip_req&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OS_ROM_URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_rom_zip_req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Parse the body as a zip file
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip_req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There should only be one file in the archive which is the one we want:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Archive contents: '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;','&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;namelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Archive contents: OS12.ROM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, we want to get the first 0x300 bytes:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;font_table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_rom_zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os_rom_zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filelist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])[:&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bitarray&lt;/code&gt; module along with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;numpy&lt;/code&gt; to extract the character
font as a 1-bit array. The characters are stored as one byte per row and so our
final font array should be 8 columns wide and 8 × number of characters
high.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bitarray&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitarray&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;font_table_array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bitarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endian&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'big'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;font_table_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frombytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;font&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font_table_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tolist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;n_chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Number of characters: {0}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n_chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Number of characters: 96
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s look at the first few characters just to check:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matplotlib&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f344fc0db38&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2015-02-03-font-from-bbc-rom_files/2015-02-03-font-from-bbc-rom_17_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It would be nice to see the entire font as one image. Let’s first split it into
one array per character:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;char_arrays&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n_chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# check that we've split correctly
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char_arrays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f344fbebd68&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2015-02-03-font-from-bbc-rom_files/2015-02-03-font-from-bbc-rom_19_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we do some advanced juggling to reshape the character array:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;font_image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vstack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hstack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char_arrays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Generated font image of shape: '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;font_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Generated font image of shape: 48x128





&amp;lt;matplotlib.image.AxesImage at 0x7f344fb65208&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2015-02-03-font-from-bbc-rom_files/2015-02-03-font-from-bbc-rom_21_2.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And we’re a winner. The only thing left to do is to save it as an image for some
further processing. We’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imgurpython&lt;/code&gt; library to upload our result
directly to imgur. I’ve created a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imgur-credentials.json&lt;/code&gt;
containing the client id and secret I obtained by registering an application at
https://api.imgur.com/. The file looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;clientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;some magic hex string&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;clientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;another magic hex string&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first thing to do is to create an imgur client to upload the file:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;json&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;imgurpython&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImgurClient&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;imgur_creds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'imgur-credentials.json'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imgur_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImgurClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imgur_creds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'clientId'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imgur_creds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'clientSecret'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pillow&lt;/code&gt; library we can save the image to a temporary file and the
upload it. Note the use of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt; statement to make sure that the
temporary file is deleted.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tempfile&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NamedTemporaryFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'.png'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;font_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;upload_result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imgur_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upload_from_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Image uploaded to: {0}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upload_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'link'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Image uploaded to: http://i.imgur.com/7mZVBee.png
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;IPython has some built-in support for displaying images from URLs. Let’s use
that to look at the result:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IPython.display&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upload_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'link'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2015-02-03-font-from-bbc-rom_files/2015-02-03-font-from-bbc-rom_27_0.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This little post has shown how we can extract fonts directly from old
microcomputer ROM images with Python. As a bonus, we upload the resulting font
back to the web via the imgur API.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><category term="python" /><category term="notebook" /><summary type="html">This post is also available as an IPython notebook which may be downloaded or viewed online.</summary></entry><entry><title type="html">Optical Character Recognition in Python: Transcribing the Turing code</title><link href="http://www.richwareham.com//ocr-in-python/" rel="alternate" type="text/html" title="Optical Character Recognition in Python: Transcribing the Turing code" /><published>2014-11-09T00:00:00+00:00</published><updated>2014-11-09T00:00:00+00:00</updated><id>http://www.richwareham.com//ocr-in-python</id><content type="html" xml:base="http://www.richwareham.com//ocr-in-python/">&lt;p class=&quot;preamble&quot;&gt;This post is also available as an IPython notebook which may be
&lt;a href=&quot;/downloads/2014-11-09-ocr-in-python.ipynb&quot;&gt;downloaded&lt;/a&gt;
or &lt;a href=&quot;/downloads/2014-11-09-ocr-in-python.ipynb&quot;&gt;viewed online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post talks about how to do a simple Optical Character Recognition task in
Python. But first, why did I want to do this in the first place?&lt;/p&gt;

&lt;p&gt;The University of Manchester is, as I’m writing this, running &lt;a href=&quot;ht
tp://www.maths.manchester.ac.uk/cryptography_competition_the_imitation_game/inde
x.php&quot;&gt;a competition&lt;/a&gt; as part of a tie in with the forthcoming film &lt;em&gt;The Imitiation Game&lt;/em&gt;. The
film centres around Alan Turing and his time as a cryptographer at Bletchley
Park near Oxford.&lt;/p&gt;

&lt;p&gt;Appropriately enough, the competition involves decrypting (or deciphering) a set
of clues which it is claimed will reveal the location where Alan Turing hid some
bars of silver.&lt;/p&gt;

&lt;p&gt;The first and third problems are straightforward enough and I’ll not spoil the
competition by posting solutions here.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the second problem:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Import PyLab to generate plots and set the default figure size
# to something a little nicer.
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pylab&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rcParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'figure.figsize'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Use PIL and requests to fetch the second clue:
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://www.maths.manchester.ac.uk/cryptography_competition_the_imitation_game/code2_full_res.png'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;io&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BytesIO&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;code2_im&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Populating the interactive namespace from numpy and matplotlib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s take a look at the clue:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code2_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0cb0374410&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_4_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, none of the characters here are the usual alphabetic ones.
There’s an eight and a three but the rest are mathematical symbols. Before I can
start doing any decrypting, I need to transcribe these symbols. I could do this
manually but a) I don’t know which letters to use for which symbols and b) this
is highly error prone. Instead, I wondered if I could write a Python program to
extract a list of symbols for me.&lt;/p&gt;

&lt;h1 id=&quot;extracting-images-for-each-symbol&quot;&gt;Extracting images for each symbol&lt;/h1&gt;

&lt;p&gt;The first thing to do is snip an image containing each symbol in the input.
Counting them, I see the grid is 24 symbols across and 15 down. Let’s try to
form an array of centre pixel locations for each symbol. Using IPython, I can
fidle around with the values below until the dots line up roughly with the
symbols:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Fiddle with these numbers a bit...
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meshgrid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;93&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1146&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;135&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;984&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Until these dots lie at the centres
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code2_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;scatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.collections.PathCollection at 0x7f0cb02add90&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_8_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;OK, those points look good. Let’s snip out 16 pixels either side of these centre
points into one “region” per symbol:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Get the image as a grayscale image
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code2_grey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code2_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'F'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# region box &quot;half width&quot;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bhw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code2_grey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bhw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bhw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bhw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bhw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    
&lt;span class=&quot;c1&quot;&gt;# Since the final line only has 9 symbols on it, ignore the final
# 15 regions (set them to blank)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zeros_like&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Number of regions: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Number of regions: 360
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also write a little convenience function to view all of these snipped out
regions:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Tile arrays from *mats* into a larger array optionally
    specifying the number of rows.
    
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ceil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cols&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;snipped_regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;snipped_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0cb0222390&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_13_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Excellent, they look nice. The next step will be to match each region to a
corresponding “symbol id”.&lt;/p&gt;

&lt;h1 id=&quot;computing-feature-vectors&quot;&gt;Computing feature vectors&lt;/h1&gt;

&lt;p&gt;We can’t directly compare the pixel values in each region. It is likely that my
rough centre-points are not exact and there is a non-uniform background in the
image. (I.e. the “wartime paper” effect.) Instead we want to manipulate the
images to exctract a vector of features which are tolerant to small shifts or
small changes in intensity. Fortunately I have just a thing in my little bag of
tricks a &lt;a href=&quot;https://github.com/rjw57/dtcwt&quot;&gt;Python implementation of the Dual-Tree Complex Wavelet
Transform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s not required to know the specifics of the transform but see what happens if
we transform the first region and one translated by one pixel:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Create a &quot;default&quot; DTCWT
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dtcwt&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform2d&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dtcwt_2d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Transform2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Transform the first region and that region shifted a little
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1_im&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtcwt_2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;t2_im&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;t2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtcwt_2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t2_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Show images side-by-side
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t2_im&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0cb01af050&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_17_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Again, the technicalities of the transform are not of interest to us here but
the DTCWT converts an image into a “lowpass” (or “blurred”) low-resolution image
and a set of “highpass” (or “edge-like”) complex-valued images.&lt;/p&gt;

&lt;p&gt;Each highpass image is an array of NxMx6 complex numbers. Let’s take a look at a
slice from the first symbol:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Look at first slice of second (level &quot;3&quot;)) highpass image
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;highpasses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][...,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;highpasses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][...,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0cb00f6950&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_19_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Although the images have been shifted a little, the DTCWT coefficients are very
similar. We’ll use this to perform matching. Firstly, transform each region into
a “feature vector” consisting of the DTCWT highpass coefficients from level 3
onwards:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compute_region_fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t_region&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtcwt_2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;concatenate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ravel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t_region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;highpasses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compute_region_fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Feature vectors has shape: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Feature vectors has shape: (360, 96)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;matching-symbols&quot;&gt;Matching symbols&lt;/h1&gt;

&lt;p&gt;We need a set of symbols to match with and so I manually went through the image
and found the indices of the first example of each kind of symbol:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Indices of &quot;exemplar&quot; regions
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;57&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;102&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;143&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;322&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Final &quot;blank&quot; region
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Symbol count: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Symbol count: 30
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s look at them:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_idx&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpolation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'none'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0cb004bfd0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_26_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We’re going to match regions by computing the total difference between the DTCWT
coefficients and those of the examplar regions. Then we’ll assign that region a
symbol id based on which exemplar it is closest to.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Compute total differences to each exemplar for each region
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_idx&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Get exemplar's feature vectore
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;e_fv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_idx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,:]&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Difference from each feature vector to the exemplar
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e_fv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;feat_vecs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Compute total absolute difference
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
&lt;span class=&quot;c1&quot;&gt;# Use Numpy's argmin function to return the index of the smallest difference
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diffs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Has it worked? Let’s find out. We’ll create a “reconstructed” image by copying
the exemplar symbol for each map into the appropriate position in the output
image:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;synthetic_snipped_regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exemplars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m_idx&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synthetic_snipped_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0caba37e90&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_30_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That looks pretty good. What about if we compute the difference between this and
the original:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synthetic_snipped_regions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snipped_regions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'gray'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f0caba1d710&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-11-09-ocr-in-python_files/2014-11-09-ocr-in-python_32_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Looks good. If we had got any matches wrong we would have a very strong white or
black line where the matched symbol differed from the actual. The final step is
to turn the match indexes into a transcription. We’ll write out the code again
using the symbol index in place of the symbol:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;''&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'{0:0&amp;gt;2} '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00 01 02 03 04 05 02 06 05 01 06 07 08 09 10 11 12 13 14 04 15 04 04 05 
13 10 16 04 13 07 17 04 13 14 03 18 13 19 07 00 01 14 13 12 17 14 09 10 
04 20 21 22 12 25 09 20 02 23 13 19 24 15 04 24 09 10 18 03 12 16 09 20 
02 20 08 16 05 12 13 23 07 09 06 08 25 16 09 13 19 02 05 18 10 05 13 25 
04 26 00 13 12 22 27 13 12 24 09 10 14 06 12 10 27 27 13 12 24 15 27 13 
04 02 01 15 11 13 19 02 05 07 20 02 02 09 20 12 07 09 03 08 09 19 08 28 
15 16 03 18 13 19 08 07 13 18 12 10 03 17 24 09 10 04 06 21 06 02 10 04 
25 26 00 06 16 15 11 24 12 03 04 16 25 07 26 02 13 12 15 06 16 10 11 13 
04 05 11 02 19 20 24 13 27 25 04 05 07 09 10 16 25 02 21 20 12 27 13 02 
02 13 14 16 15 08 15 16 09 25 05 05 10 04 22 03 16 24 13 27 16 24 06 04 
02 10 01 12 13 06 05 23 06 12 07 13 27 00 01 12 10 26 19 02 06 12 11 01 
11 02 10 12 13 19 08 20 24 13 14 13 12 17 04 13 08 20 07 13 16 22 02 27 
27 15 04 03 02 11 13 05 22 10 04 11 12 01 23 24 20 05 18 01 06 17 12 15 
10 26 16 00 03 12 25 04 10 00 29 00 03 11 09 15 04 22 19 16 15 04 26 00 
01 25 04 25 24 25 06 02 16 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, maybe, it’s easier if we use alphabetic characters. (Note that we’ll also
end up using some punctuation since there are more than 26 symbols.)&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;''&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match_idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'{0} '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s_idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transcription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;A B C D E F C G F B G H I J K L M N O E P E E F 
N K Q E N H R E N O D S N T H A B O N M R O J K 
E U V W M Z J U C X N T Y P E Y J K S D M Q J U 
C U I Q F M N X H J G I Z Q J N T C F S K F N Z 
E [ A N M W \ N M Y J K O G M K \ \ N M Y P \ N 
E C B P L N T C F H U C C J U M H J D I J T I ] 
P Q D S N T I H N S M K D R Y J K E G V G C K E 
Z [ A G Q P L Y M D E Q Z H [ C N M P G Q K L N 
E F L C T U Y N \ Z E F H J K Q Z C V U M \ N C 
C N O Q P I P Q J Z F F K E W D Q Y N \ Q Y G E 
C K B M N G F X G M H N \ A B M K [ T C G M L B 
L C K M N T I U Y N O N M R E N I U H N Q W C \ 
\ P E D C L N F W K E L M B X Y U F S B G R M P 
K [ Q A D M Z E K A ^ A D L J P E W T Q P E [ A 
B Z E Z Y Z G C Q _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h1&gt;

&lt;p&gt;We’ve successfully used Python to avoid the tedium of having to transcribe a
sheet of symbols by hand. Techniques such as this are used by actual OCR
programs to be tolerant to small changes in character shape. Obviously I won’t
be writing any more about the further decoding of this clue until after the
closing date for the competition :).&lt;/p&gt;</content><author><name></name></author><summary type="html">This post is also available as an IPython notebook which may be downloaded or viewed online.</summary></entry><entry><title type="html">Making a Little Planet Panorma using Python and Scikit Image</title><link href="http://www.richwareham.com//little-planet-projection/" rel="alternate" type="text/html" title="Making a Little Planet Panorma using Python and Scikit Image" /><published>2014-09-29T00:00:00+00:00</published><updated>2014-09-29T00:00:00+00:00</updated><id>http://www.richwareham.com//little-planet-projection</id><content type="html" xml:base="http://www.richwareham.com//little-planet-projection/">&lt;p class=&quot;preamble&quot;&gt;This post is also available as an IPython notebook which may be
&lt;a href=&quot;/downloads/2014-09-29-little-planet-projection.ipynb&quot;&gt;downloaded&lt;/a&gt;
or &lt;a href=&quot;/downloads/2014-09-29-little-planet-projection.ipynb&quot;&gt;viewed online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently I shared &lt;a href=&quot;https://plus.google.com/+RichWareham/posts/a9X2cGV9oDk&quot;&gt;a
post&lt;/a&gt; on G+ which had a
Google “photosphere” transformed into the “little planet” projection. This post
(and associated IPython notebook) will walk you through the way I created the
planet image using &lt;a href=&quot;http://scikit-image.org/&quot;&gt;scikit-image&lt;/a&gt;. This was my first
steps with scikit-image since previously I had been using
&lt;a href=&quot;http://opencv.org/&quot;&gt;OpenCV&lt;/a&gt;. Unfortunately OpenCV is a bit of a pain to install
and so I was looking for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;-installable library with the functionality I
required.&lt;/p&gt;

&lt;h1 id=&quot;loading-the-initial-panorama&quot;&gt;Loading the initial panorama&lt;/h1&gt;

&lt;p&gt;As usual, we need a little bit of boiler-plate to locad matplotlib and numpy.
I’m also going to import the &lt;a href=&quot;http://pillow.readthedocs.org/en/latest/&quot;&gt;Pillow&lt;/a&gt;
library for loading images (although I think scikit-image could do that).&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# IPython magic to allow inline plots
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matplotlib&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# for file and pathname handling functions
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;PIL&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Set the default matplotlib figure size to be a bit bigger than default
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rcParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'figure.figsize'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On my phone, the photosphere is stored as a single JPEG file. Let’s use Pillow
to load the image and display it via matplotlib:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expanduser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'~/Downloads/PANO_20140927_124513.jpg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f91a1106090&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_5_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Beautiful. The next step is to work out how to warp this image.&lt;/p&gt;

&lt;h1 id=&quot;image-warping-via-scikit-image&quot;&gt;Image warping via scikit-image&lt;/h1&gt;

&lt;p&gt;Looking at the &lt;a href=&quot;http://scikit-
image.org/docs/0.10.x/api/skimage.transform.html#warp&quot;&gt;appropriate section&lt;/a&gt; in the scikit-learn
documentation, we see that there is a handy function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warp&lt;/code&gt; which will
do exactly what we want. The function takes, amongst other inputs, the image to
warp, the output image shape and a function which specifies the warp itself.&lt;/p&gt;

&lt;p&gt;This function takes a N⨉2 array of (x,y) co-ordinates in the &lt;em&gt;output image&lt;/em&gt; and
turns them into a corresponding array of co-ordinates in the &lt;em&gt;input image&lt;/em&gt;.
Let’s just have a go at that using a very simple function which scales and
shifts the co-ordinates:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;skimage.transform&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scale_by_5_and_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# A square figure for square output
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scale_by_5_and_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f917c93c610&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_9_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-little-planet-projection&quot;&gt;The “little planet” projection&lt;/h1&gt;

&lt;p&gt;The panorama we have is in the “equirectangular” projection which means that the
y-co-ordinate represents the up-down angle and the x-co-ordinate represents the
left-right angle. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Stereographic_projection#Photography&quot;&gt;little
planet&lt;/a&gt;
projection takes a different approach: the up-down angle is now specified by the
distance from the centre of the image and the left-right angle is given by the
angle from the horizontal in the image.&lt;/p&gt;

&lt;p&gt;Given a particular output image shape, therefore, we can convert a co-ordinate
in that image to a radius and angle, or 𝑟 and 𝜃 pair:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# What shape will the output be?
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# rows x columns
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;output_coord_to_r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Convert co-ordinates in the output image to r, theta co-ordinates.
    The r co-ordinate is scaled to range from from 0 to 1. The theta
    co-ordinate is scaled to range from 0 to 1.
    
    A Nx2 array is returned with r being the first column and theta being
    the second.
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Calculate x- and y-co-ordinate offsets from the centre:
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;x_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Calculate r and theta in pixels and radians:
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arctan2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# The maximum value r can take is the diagonal corner:
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;max_x_offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_y_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;max_r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_x_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_y_offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Scale r to lie between 0 and 1
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_r&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# arctan2 returns an angle in radians between -pi and +pi. Re-scale
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# it to lie between 0 and 1
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Stack r and theta together into one array. Note that r and theta are initially
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 1-d or &quot;1xN&quot; arrays and so we vertically stack them and then transpose
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# to get the desired output.
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vstack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re now very nearly in a position to generate our first little planet picture.
In our original panorama 𝑟=0 corresponds to the bottom of the picture (i.e.
maximum y-co-ordinate) and 𝑟=1 corresponds to the top (i.e. a y-co-ordinate of
zero). Similarly 𝜃=0 corresponds to an x-co-ordinate of 0 and 𝜃=1 corresponds to
the maximum x-co-ordinate. We can write a function to convert these co-
ordinates:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This is the shape of our input image
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_shape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shape&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;r_theta_to_input_coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Convert a Nx2 array of r, theta co-ordinates into the corresponding
    co-ordinates in the input image.
    
    Return a Nx2 array of input image co-ordinates.
    
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Extract r and theta from input
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Theta wraps at the side of the image. That is to say that theta=1.1
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# is equivalent to theta=0.1 =&amp;gt; just extract the fractional part of
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# theta
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Calculate the maximum x- and y-co-ordinates
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;max_x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Calculate x co-ordinates from theta
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_x&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Calculate y co-ordinates from r noting that r=0 means maximum y
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# and r=1 means minimum y
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_y&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Return the x- and y-co-ordinates stacked into a single Nx2 array
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hstack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s test our mapping functions using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;warp&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;little_planet_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Chain our two mapping functions together.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_coord_to_r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta_to_input_coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;little_planet_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f917c8ab2d0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_16_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s not a bad first attempt but it would be nicer if we had a bit more
horizon and a little less ground. That is to say we’d like to map 𝑟 a bit so
that it increases quite rapidly to start with but flattens off a little.
Usefully the square root function behaves a little like that:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&amp;lt;matplotlib.lines.Line2D at 0x7f917c88e7d0&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_18_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So let’s modify our little planet projection to take the square root of 𝑟:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;little_planet_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Chain our two mapping functions together with modified r.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_coord_to_r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Take square root of r
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta_to_input_coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;little_planet_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f917c7eeb90&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_20_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s better. The castle and trees look less warped. It would be nice to have
the castle come out at a slightly different angle though. We can do that by
shifting theta:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;little_planet_3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Chain our two mapping functions together with modified r
    and shifted theta.
    
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_coord_to_r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Take square root of r
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Shift theta
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta_to_input_coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;little_planet_3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f917c7d4790&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_22_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s nicer. There’s possibly a little too much sky so, finally, let’s just
zooom in a bit by scaling $r$ down a bit:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;little_planet_4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Chain our two mapping functions together with modified and
    scaled r and shifted theta.
    
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_coord_to_r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Scale r down a little to zoom in
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Take square root of r
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Shift theta
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r_theta_to_input_coords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r_theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_coords&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;figsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;little_planet_4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;matplotlib.image.AxesImage at 0x7f917c738290&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_24_1.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;saving-the-result&quot;&gt;Saving the result&lt;/h1&gt;

&lt;p&gt;I’m happy with this image now. Let’s use Pillow to save the result:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Compute final warped image
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano_warp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;little_planet_4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output_shape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# The image is a NxMx3 array of floating point values from 0 to 1. Convert this to
# bytes from 0 to 255 for saving the image:
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano_warp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pano_warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uint8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Use Pillow to save the image
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pano_warp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expanduser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'~/Pictures/little-planet.jpg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;IPython can display images directly fiven a filename. Let’s just check it saved
correctly:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;IPython.display&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;display_Image&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;display_Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expanduser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'~/Pictures/little-planet.jpg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-29-little-planet-projection_files/2014-09-29-little-planet-projection_29_0.jpeg&quot; alt=&quot;jpeg&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;In this post we gave a quick example of how to interactively play with an image
and warp it from an equirectangular projection to the popular “little planet”
projection.&lt;/p&gt;</content><author><name></name></author><summary type="html">This post is also available as an IPython notebook which may be downloaded or viewed online.</summary></entry><entry><title type="html">Verifying identity on social media with keybase.io and command-line tools</title><link href="http://www.richwareham.com//verifying-identity-with-keybase-io/" rel="alternate" type="text/html" title="Verifying identity on social media with keybase.io and command-line tools" /><published>2014-09-24T00:00:00+00:00</published><updated>2014-09-24T00:00:00+00:00</updated><id>http://www.richwareham.com//verifying-identity-with-keybase-io</id><content type="html" xml:base="http://www.richwareham.com//verifying-identity-with-keybase-io/">&lt;p&gt;I recently joined &lt;a href=&quot;https://keybase.io&quot;&gt;keybase.io&lt;/a&gt; which is an interesting
experiment in verifiable assertions of identity on social media. The concept is
simple: if I tweet &lt;a href=&quot;https://twitter.com/richwareham/status/514478671852089346&quot;&gt;a
message&lt;/a&gt; which can
be verified as being signed by my private key then you can trust that, at that
point, my twitter account was controlled by someone who also had access to my
private key. The same assertions can be made about GitHub accounts by signing
&lt;a href=&quot;https://gist.github.com/rjw57/f2f7b7b2da6ed3cc149c&quot;&gt;a gist&lt;/a&gt; which makes a
similar statement.&lt;/p&gt;

&lt;p&gt;Assuming that one trusts a user to be savvy enough to revoke these assertions
if they believe their private key is compromised, this provides a fairly strong
assertion that various social media accounts are controlled by the same entity.
Of course one could do this already oneself using
&lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_Privacy_Guard&quot;&gt;GPG&lt;/a&gt; but keybase.io provides
a relatively nice workflow and a pretty website. Do not underestimate the
advantages which this can bring.&lt;/p&gt;

&lt;h1 id=&quot;why-is-this-useful&quot;&gt;Why is this useful?&lt;/h1&gt;

&lt;p&gt;I must admit that I do not have a long list of reasons why it is useful to tie
social media account identities together and associate them with a public key
apart from it being a sort of social media “verified tick” which is available
to anyone.&lt;/p&gt;

&lt;p&gt;That being said, there are a few use cases I can think of. If one is a software
developer which makes signed releases but is otherwise anonymous, e.g.
TrueCrypt, then it would be useful to assert that a twitter account is
controlled by the same person. To flip this in the other direction, if you know
that a Twitter account is controlled by someone with access to a private key
then you can have some confidence that anything signed by that private key came
from the same person. Finally, it’s useful to be able to say that
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@john_q_rockstar&lt;/code&gt; on Twitter is the same as the person running
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://johnqrockstar.com/&lt;/code&gt; without relying on there being a Twitter
“verified” tick.&lt;/p&gt;

&lt;h1 id=&quot;so-how-does-it-work&quot;&gt;So, how does it work?&lt;/h1&gt;

&lt;p&gt;The central keybase.io concept is that one should be able to verify for oneself
that a social media account or DNS domain was at some point controlled by
someone in possession of the private half of a public key which you trust.
Let’s work through an example of verifying that &lt;a href=&quot;https://pgp.mit.edu/pks/lookup?op=vindex&amp;amp;search=0x7BA6A2C74E3D7E0D&quot;&gt;my public
key&lt;/a&gt; is
associated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt;. (Note that
&lt;a href=&quot;https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions&quot;&gt;DNSSEC&lt;/a&gt;
does not solve this exact problem although it certainly works in a related
space.)&lt;/p&gt;

&lt;p&gt;It would be nice to be able to verify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt; without trusting
keybase.io infrastructure to actually do the verification. In that way,
keybase.io can be seen as a nice tool for creating the verifications without
being a critical, or trusted, component in the chain.&lt;/p&gt;

&lt;h2 id=&quot;importing-the-public-key&quot;&gt;Importing the public key&lt;/h2&gt;

&lt;p&gt;The first thing that one needs to do is to import my public key. Obviously I
already have mine imported but one can always import it again:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; https://keybase.io/richwareham/key.asc | gpg &lt;span class=&quot;nt&quot;&gt;--import&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;gpg: key 4E3D7E0D: &quot;Rich Wareham (Personal) &amp;lt;rjw57@cantab.net&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; not changed
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;gpg: Total number processed: 1
gpg:              unchanged: 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s just check the fingerprint of that key:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--fingerprint&lt;/span&gt; rjw57@cantab.net
&lt;span class=&quot;go&quot;&gt;pub   4096R/4E3D7E0D 2011-10-12
      Key fingerprint = 4E27 6278 B082 CE98 E5A9  618E 7BA6 A2C7 4E3D 7E0D
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;uid                  Rich Wareham (Personal) &amp;lt;rjw57@cantab.net&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;sub   4096R/4D4C8881 2011-10-12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This fingerprint can be verified against both my &lt;a href=&quot;https://keybase.io/richwareham&quot;&gt;keybase.io profile
page&lt;/a&gt; and the &lt;a href=&quot;https://pgp.mit.edu/pks/lookup?op=vindex&amp;amp;search=0x7BA6A2C74E3D7E0D&quot;&gt;MIT public key
server&lt;/a&gt;. It
should also appear in your web of trust if you have any trust in the keys which
signed mine.&lt;/p&gt;

&lt;h2 id=&quot;getting-the-assertion-from-dns&quot;&gt;Getting the assertion from DNS&lt;/h2&gt;

&lt;p&gt;We now need to see what is asserted in DNS. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dig&lt;/code&gt; tool to get
any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TXT&lt;/code&gt; records from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;dig txt richwareham.com | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;keybase
&lt;span class=&quot;go&quot;&gt;richwareham.com.        7687    IN      TXT     &quot;keybase-site-verification=AnLTOBDGpT9Efr0DJn4X4C7VcnwZF_ZRT6Vx8_pk_J8&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important record here is the one containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keybase-site-verification&lt;/code&gt;.
This also contains the following apparently magic string:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;AnLTOBDGpT9Efr0DJn4X4C7VcnwZF_ZRT6Vx8_pk_J8&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-the-assertion-actually-is&quot;&gt;What the assertion actually is&lt;/h2&gt;

&lt;p&gt;On the &lt;a href=&quot;https://keybase.io/richwareham/sigs/AnLTOBDGpT9Efr0DJn4X4C7VcnwZF_ZRT6Vx&quot;&gt;associated proof
page&lt;/a&gt;
for my DNS assertion you can see a walk through of how that magic string was
generated. It indicates that everything started from a JSON-formatted assertion
message which was signed by my public key. Let’s use GPG to verify that by
pasting in the GPG signed statement from the website:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;gpg &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;-----BEGIN PGP MESSAGE-----
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;Version: GnuPG v1
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;owGbwMvMwMRYvWzRcT/bOl7G0we+JDGEKO5kqVZKyk+pVLKqVspOBVNpmXnpqUUF
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;RZl5JUpWSiapRuZmRuYWSQYWRsmplhappomWZoYWqeZJiWaJRsnmJqnGKeapBilK
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;OkoZ+cUgHUBjkhKLU/Uy84FiQE58ZgpQFIv6UoiEiXmqRaqFgaGhhWGyibmxqbmx
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;iXmyWaKZmZm5oYmBAUhhcWpRXmJuKlB1UWZyRnliUWpGYq5SrY4SUKIsMzkV5OyU
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/NzEzDxUJXrJ+blA/QVF+SX5yfk5QMmUvGKQvpLKApBp5alJ8VAj4pMy81KAPgcq
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;L0stKs7MBxplCFSZXJIJstjQxNDQBOhxSyMdpdSKgsyi1HiQZYam5mYWBkAAsiS1
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;DGikAdAzZqmJhsaJyZbmqWlmZmmmiYaWyQZJaeYmKSlJySYmaSlJxkZmJolGaQZJ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;RkmJiUkGBqYGlompBubGFoapSiBPFeblK1kZA52ZmA40sjgzPS+xpLQoVamWq5NJ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;hoWBkYmBjZUJFHkMXJwCsChVOsz/z7JYubSvKVrRcRKfrdRDrYenTvLc9vr2fcnq
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;VPuHGeuV2apPxnP94vM3PZcRKCjfH7rxo4PY89Iz0xZ5HLPdpfPgtimHy5u+jZ+i
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;JbjWn3WyUVz1o+73Kle7oN/FJgXuERN+ap7mddU1M5Y5+3j2ujkznPW63Jbedb5V
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;4129ke9m563VRyMvPeVYfUhfo3zzzxBxUTmFtfsP/ou5qfBv+R9TC83jry+39WvL
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;mqrf/r9j93Zln+6QS1kc3qveahSzyy7426Jx/cDpW/POPqhnXvVRt9OtwbvM686c
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;3La7lXOOci7L0XpgnB+9PlE9ztk9SfpK3JZfrtMLV32fkKn80XRS3T7fg0Lvgk88
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;zjLwy11SeGT+z9mtmo8ebJubUnDnw4IghdT5fr9DZ8w/p3Sc+2Tcct7NTkInxaUZ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;HfQy3wVPvVpvWFWlHPn/1fSiYz+ynpnfUZ+wq/nhy05rU/np3IcMze8cO/V60ctl
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;By7xlTqYmqVYndve7bxLZV1TKP86s/1aCxYwf31//d3a/a9VG+7t7is8KpmTIuC3
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;I7egrTU7M7ajOnfO7xNbLlo48H0Ta70yb5q33IV5/NtrK2Q0OHv23/fRl/IL3KXy
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;uWHBuxZHWavSiXZPTKzY9D3fy+yZ2mWl3STNs3fJvpv/dqqF5kytS1t7esYu9oqX
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;Z39fWa/svfjzQpcLwR5652bfvGBixnb+YG5J7B+hWWX1lwE=
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;=bND4
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;-----END PGP MESSAGE-----
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;{&quot;body&quot;:{&quot;key&quot;:{&quot;fingerprint&quot;:&quot;4e276278b082ce98e5a9618e7ba6a2c74e3d7e0d&quot;,&quot;host&quot;:&quot;keybase.io&quot;,&quot;key_id&quot;:&quot;7ba6a2c74e3d7e0d&quot;,&quot;uid&quot;:&quot;747e8e801181c47357347c6a66671400&quot;,&quot;username&quot;:&quot;richwareham&quot;},&quot;service&quot;:{&quot;domain&quot;:&quot;richwareham.com&quot;,&quot;protocol&quot;:&quot;dns&quot;},&quot;type&quot;:&quot;web_service_binding&quot;,&quot;version&quot;:1},&quot;ctime&quot;:1411496192,&quot;expire_in&quot;:157680000,&quot;prev&quot;:&quot;0e8e6ea13ac97ef66f5a19c0bf74ddbc44fdb3264a2f0b2baab00509ae07381e&quot;,&quot;seqno&quot;:3,&quot;tag&quot;:&quot;signature&quot;}
gpg: Signature made Tue 23 Sep 2014 19:16:36 BST using RSA key ID 4E3D7E0D
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;gpg: Good signature from &quot;Rich Wareham (Personal) &amp;lt;rjw57@cantab.net&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Indeed this is a message signed by my key. The message includes a fingerprint
of my public key, my keybase.io username, the DNS domain I was asserting is
mine and when I made the assertion. OK so I trust that I actually asserted once
that I control &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt;. But I could just as easily assert I control
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.com&lt;/code&gt;. We need to use the TXT record on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt; to
actually check this.&lt;/p&gt;

&lt;h2 id=&quot;getting-the-expected-hash&quot;&gt;Getting the expected hash&lt;/h2&gt;

&lt;p&gt;The next stage is to take the actual signed message and compute a
&lt;a href=&quot;https://en.wikipedia.org/wiki/SHA-2&quot;&gt;SHA-256&lt;/a&gt; hash of its binary
representation. Fortunately that’s pretty easy. Stripped of the header and
footer, a GPG message is just a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Base64&quot;&gt;Base64&lt;/a&gt;-encoded binary blob. We can use
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base64&lt;/code&gt; utility to decode it, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256sum&lt;/code&gt; utility to compute the
SHA-256 hash of the contents and then use the ever-wonderful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxd&lt;/code&gt; utility to
convert the hex-string produced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sha256sum&lt;/code&gt; into a binary file containing
the 256-bits (or 32-bytes) of hash:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOL&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; | sha256sum - | xxd -r -p expected.bin
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;owGbwMvMwMRYvWzRcT/bOl7G0we+JDGEKO5kqVZKyk+pVLKqVspOBVNpmXnpqUUF
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;RZl5JUpWSiapRuZmRuYWSQYWRsmplhappomWZoYWqeZJiWaJRsnmJqnGKeapBilK
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;OkoZ+cUgHUBjkhKLU/Uy84FiQE58ZgpQFIv6UoiEiXmqRaqFgaGhhWGyibmxqbmx
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;iXmyWaKZmZm5oYmBAUhhcWpRXmJuKlB1UWZyRnliUWpGYq5SrY4SUKIsMzkV5OyU
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/NzEzDxUJXrJ+blA/QVF+SX5yfk5QMmUvGKQvpLKApBp5alJ8VAj4pMy81KAPgcq
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;L0stKs7MBxplCFSZXJIJstjQxNDQBOhxSyMdpdSKgsyi1HiQZYam5mYWBkAAsiS1
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;DGikAdAzZqmJhsaJyZbmqWlmZmmmiYaWyQZJaeYmKSlJySYmaSlJxkZmJolGaQZJ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;RkmJiUkGBqYGlompBubGFoapSiBPFeblK1kZA52ZmA40sjgzPS+xpLQoVamWq5NJ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;hoWBkYmBjZUJFHkMXJwCsChVOsz/z7JYubSvKVrRcRKfrdRDrYenTvLc9vr2fcnq
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;VPuHGeuV2apPxnP94vM3PZcRKCjfH7rxo4PY89Iz0xZ5HLPdpfPgtimHy5u+jZ+i
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;JbjWn3WyUVz1o+73Kle7oN/FJgXuERN+ap7mddU1M5Y5+3j2ujkznPW63Jbedb5V
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;4129ke9m563VRyMvPeVYfUhfo3zzzxBxUTmFtfsP/ou5qfBv+R9TC83jry+39WvL
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;mqrf/r9j93Zln+6QS1kc3qveahSzyy7426Jx/cDpW/POPqhnXvVRt9OtwbvM686c
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;3La7lXOOci7L0XpgnB+9PlE9ztk9SfpK3JZfrtMLV32fkKn80XRS3T7fg0Lvgk88
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;zjLwy11SeGT+z9mtmo8ebJubUnDnw4IghdT5fr9DZ8w/p3Sc+2Tcct7NTkInxaUZ
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;HfQy3wVPvVpvWFWlHPn/1fSiYz+ynpnfUZ+wq/nhy05rU/np3IcMze8cO/V60ctl
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;By7xlTqYmqVYndve7bxLZV1TKP86s/1aCxYwf31//d3a/a9VG+7t7is8KpmTIuC3
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;I7egrTU7M7ajOnfO7xNbLlo48H0Ta70yb5q33IV5/NtrK2Q0OHv23/fRl/IL3KXy
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;uWHBuxZHWavSiXZPTKzY9D3fy+yZ2mWl3STNs3fJvpv/dqqF5kytS1t7esYu9oqX
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;Z39fWa/svfjzQpcLwR5652bfvGBixnb+YG5J7B+hWWX1lwE=
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; expected.bin
&lt;span class=&quot;go&quot;&gt;00000000  02 72 d3 38 10 c6 a5 3f  44 7e bd 03 26 7e 17 e0  |.r.8...?D~..&amp;amp;~..|
00000010  2e d5 72 7c 19 17 f6 51  4f a5 71 f3 fa 64 fc 9f  |..r|...QO.q..d..|
00000020
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have a file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expected.bin&lt;/code&gt;, which contains a 256 bit (32 byte) hash
of our signed assertion. To prove that I control &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt;, I
added a TXT record which contained an encoded form of that hash.&lt;/p&gt;

&lt;h2 id=&quot;getting-the-actual-hash-from-dns&quot;&gt;Getting the actual hash from DNS&lt;/h2&gt;

&lt;p&gt;It’s asserted that the magic string we found in the DNS TXT record should be
the web-safe Base64 encoding of that very hash. The assumption being that only
someone in control of the domain would be able to insert a TXT record which is
cryptographically equivalent to their assertion of control.&lt;/p&gt;

&lt;p&gt;Web-safe Base64 is just normal Base64 with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; replaced with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;. We can use the Unix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tr&lt;/code&gt; utility to swap those characters and
decode the Base64.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'AnLTOBDGpT9Efr0DJn4X4C7VcnwZF_ZRT6Vx8_pk_J8'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-_&lt;/span&gt; +/ | &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;actual.bin
&lt;span class=&quot;go&quot;&gt;base64: invalid input
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;hexdump &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; actual.bin 
&lt;span class=&quot;go&quot;&gt;00000000  02 72 d3 38 10 c6 a5 3f  44 7e bd 03 26 7e 17 e0  |.r.8...?D~..&amp;amp;~..|
00000010  2e d5 72 7c 19 17 f6 51  4f a5 71 f3 fa 64 fc 9f  |..r|...QO.q..d..|
00000020
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid input&lt;/code&gt; warning here can be ignored since it relates to a missing
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; character on the end of the hash used to indicate the correct padding.
If you are concerned, you can verify that appending an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; to the hash
removes the warning.&lt;/p&gt;

&lt;h2 id=&quot;comparing-the-hashes&quot;&gt;Comparing the hashes&lt;/h2&gt;

&lt;p&gt;We could manually compare the hex-dumps from the expected and actual hashes but
there exists a utility whose sole purpose is to compare files so let’s use it:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;diff &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; actual.bin expected.bin
&lt;span class=&quot;go&quot;&gt;Files actual.bin and expected.bin are identical
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The hashes match and so we may conclude that I have once had control of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;richwareham.com&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;I’m not sure if keybase.io will take off as a concept but it’s a nice idea. The
technology to cryptographically assert control over social media accounts has
existed for some time but keybase.io wraps it up into a nice bundle. They also
have some rather spiffy little web-tools which allow one to verify messages
from people you follow (or “track”) on keybase.io and, if one trusts their
Javascript crypto, you can even host an encrypted private key with them.&lt;/p&gt;

&lt;p&gt;I already have a GPG key and I’d rather not upload it, even encrypted, to a
random website. Recognising this, the entire keybase verification workflow can
be done client-side on your machine using GPG. Although I signed the assertions
of control hosted on keybase.io, I did so on my machine using GPG without ever
sending the private key to them. This is the key advantage of public key
cryptosystems and so it’s nice to see keybase.io supporting this.&lt;/p&gt;

&lt;p&gt;I’m still waiting to see what the killer use-case for it will turn out to be.&lt;/p&gt;</content><author><name></name></author><summary type="html">I recently joined keybase.io which is an interesting experiment in verifiable assertions of identity on social media. The concept is simple: if I tweet a message which can be verified as being signed by my private key then you can trust that, at that point, my twitter account was controlled by someone who also had access to my private key. The same assertions can be made about GitHub accounts by signing a gist which makes a similar statement.</summary></entry><entry><title type="html">Windows Development from a Linux Programmer’s Perspective</title><link href="http://www.richwareham.com//windows-development-from-a-linux-perspective/" rel="alternate" type="text/html" title="Windows Development from a Linux Programmer’s Perspective" /><published>2014-09-17T00:00:00+00:00</published><updated>2014-09-17T00:00:00+00:00</updated><id>http://www.richwareham.com//windows-development-from-a-linux-perspective</id><content type="html" xml:base="http://www.richwareham.com//windows-development-from-a-linux-perspective/">&lt;p&gt;I recently had to do a little bit of Windows development after having managed
to avoid it for around seven years. This post documents my experiences from the
point of view of an experienced software developer from a non-Windows
background.&lt;/p&gt;

&lt;p&gt;The precise reasoning behind why I needed to do some Windows development isn’t
that important for this post but, for the curious, I’ve included a brief
summary at the end of the post. The short-version: I wanted to write a server
which would advertise itself over
&lt;a href=&quot;https://en.wikipedia.org/wiki/Zero-configuration_networking&quot;&gt;Zeroconf&lt;/a&gt; and
stream depth frames from a &lt;a href=&quot;http://www.microsoft.com/en-us/kinectforwindows/&quot;&gt;Kinect for Windows
2&lt;/a&gt; sensor over the network
using &lt;a href=&quot;https://en.wikipedia.org/wiki/%C3%98MQ&quot;&gt;ØMQ&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;dont-fight-the-inevitable&quot;&gt;Don’t fight the inevitable&lt;/h1&gt;

&lt;p&gt;It’s a mistake to try and fight the common culture on a particular platform.
My usual tool for writing research software is Python. It is an excellent
language and ecosystem for writing scientific research software on Linux-like
platforms but there is quite the impedance mismatch on Windows. I decided that
it would be best to try and embrace the Windows development culture and write a
Kinect 2 streaming server “the Windows way”.  This also meant I would avoid the
future problem of getting Python to talk to the Kinect for Windows SDK.&lt;/p&gt;

&lt;p&gt;The “Windows way” in this case is undoubtedly .NET and Visual Studio. What
follows are some of my experiences trying to determine what the Windows way is
and following it. I should note that the last time I did any Windows
programming was a long time ago in a different job and in a completely
different field. It was also in C++. I’ve very little experience with .NET
development. This was the project to learn. &lt;a href=&quot;https://github.com/rjw57/streamkinect2.net&quot;&gt;The
code&lt;/a&gt; is available. Idiom-fixing
pull requests are very welcome.&lt;/p&gt;

&lt;h1 id=&quot;visual-studio-express-edition&quot;&gt;Visual Studio Express Edition&lt;/h1&gt;

&lt;p&gt;It’s madness to charge people for the privilege of adding value to your
software platform so it’s nice to see that Microsoft make the &lt;a href=&quot;http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx&quot;&gt;express
edition&lt;/a&gt;
of Visual Studio available at zero-cost. It is, however, extremely confusing
that you need to know that you need the “for Windows Desktop” version in order
to actually develop programs; the similarly named “for Windows” edition being
good only for writing software for both users of Windows Phone OS.&lt;/p&gt;

&lt;p&gt;(Aside: perhaps Apple might want to consider quite how many times people need
to pay a $99 bounty to write code for their OS, get their development tools,
get something onto one of their phones, etc. Development on Apple used to be a
dream in 2004. Ten years later it is a nightmare.)&lt;/p&gt;

&lt;p&gt;Visual Studio itself is… OK. I don’t really like tools which hide things from
you, particularly if you’re a software developer. Visual Studio skirts the
boundary of “too much magic” but in general I didn’t find myself worrying where
on disk a particular magic bit of configuration is stored.&lt;/p&gt;

&lt;p&gt;I’m not really an IDE guy but I will admit that for an almost complete novice
.NET developer, the code-completion is useful. The quality of the MSDN
documentation, however, is pretty poor. One line summaries of methods, in
particular, are inexcusable. Also the MSDN website is really… slow… to…
browse. I’d rather have usage examples and discussion of APIs next to or within
the reference. Having them scattered all over MSDN makes quickly learning about
something pretty hard.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx&quot;&gt;MSBuild&lt;/a&gt; tool is a
welcome change since the last time I touched Windows development. Being able to
run tests, build code and even package up the results from the command line or
automated scripts is an absolute essential.&lt;/p&gt;

&lt;h1 id=&quot;c-and-net&quot;&gt;C# and .NET&lt;/h1&gt;

&lt;p&gt;I’d say that C# is a very well designed language in that it is utterly
unsurprising. Designing something new to be unsurprising is very difficult and
it is a complement to C# to describe it as such. If you can get by in Object
Pascal and Java then you’ll make yourself understood in C#. I suspect I was
programming with a thick C++ accent but I don’t think I was being &lt;em&gt;too&lt;/em&gt;
offensive to the C# idiom.&lt;/p&gt;

&lt;p&gt;I like the Go-style divorce between namespaces/modules and file names on disk;
source code files are best organised for the convenience of the developer of a
library whereas namespaces are best organised for the convenience of a
consumer. Having this distinction recognised is welcome.&lt;/p&gt;

&lt;h1 id=&quot;external-dependencies&quot;&gt;External dependencies&lt;/h1&gt;

&lt;p&gt;This has traditionally where Windows has been the most painful. Thankfully I
only had three external dependencies.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;https://github.com/zeromq/netmq&quot;&gt;NetMQ&lt;/a&gt; .NET port of ØMQ is available
through the &lt;a href=&quot;https://www.nuget.org/&quot;&gt;NuGet&lt;/a&gt; package manager. NuGet is a
solution which should’ve existed ages ago. It’s sad that it required a
third-party Open Source project to plug such a gaping hole.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Apple’s Bonjour API is exposed via COM objects. This necessitates the
installation of a MSI package to get all of Bonjour up and working but an
advantage of COM is that one doesn’t need to hard-code or discover DLL paths in
your build system.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The Kinect SDK itself is a bit of a pain. At the moment I don’t actually have
access to a Windows 8 box and so I had to do some tricks to pull the DLLs out
from the installer package directly since it flat-out refuses to install on any
other version. (I with there was an option in MSI packages to only install a
subset of the contents.) This is only necessary on my current development
machine while I wait for a Windows 8 machine to become available. The Kinect
.NET API lives in the global assembly cache (GAC) and so, again, I don’t need
to worry about hard-coding paths to find DLLs.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The handling of dependencies was actually the part of this whole project where
I was the most pleasantly surprised. NuGet has solved a lot of the problems
which I used to have with Windows development.&lt;/p&gt;

&lt;h1 id=&quot;source-control&quot;&gt;Source control&lt;/h1&gt;

&lt;p&gt;Microsoft finally woke up to the fact that git is the source control system
which the cool kids are using. And no one more obviously yearns to be one of
the cool kids than Microsoft does at the moment. Visual Studio’s git
integration is basic but functional. I didn’t try to see how easy it was to
clone/push/pull/merge/etc from the GUI because I only used the GUI for simple
“commit all the changes” type activity. For everything else, I just used &lt;a href=&quot;http://git-scm.com/download/win&quot;&gt;git
for windows&lt;/a&gt; and Git bash. There’s not much to
say about git bash: it’s git and bash. Thankfully vim is included as well. I
ended up using vim to edit things like READMEs. Visual Studio’s editor is
overkill for editing text files and I just couldn’t get on with things like
&lt;a href=&quot;http://notepad-plus-plus.org/&quot;&gt;Notepad++&lt;/a&gt;. I’m also not fashionable enough to
use Sublime Text. I suspect editors are just too personal a thing to change at
the drop of a hat.&lt;/p&gt;

&lt;p&gt;While we’re talking about git bash, the terminal (“console” in Micro-speak) is
appalling. It’s 2014 and you should be able to a) resize the window and b) copy
and paste without resorting to hidden menus. Even &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_PowerShell&quot;&gt;Windows
PowerShell&lt;/a&gt; seems to have
been hit with the same ugly stick.&lt;/p&gt;

&lt;h1 id=&quot;testing&quot;&gt;Testing&lt;/h1&gt;

&lt;p&gt;The unit test framework in Visual Studio is perfectly acceptable. It’s nice
that the test runner uses reflection to determine automatically which tests to
run. (I’m used to such a thing with testing frameworks on Python.) It’s also
nice that each test is run in its own domain meaning that silly mistakes like
dangling threads are easy to pick up on.&lt;/p&gt;

&lt;p&gt;The final code is less tested than I’d like. Only writing the server-side
portion means that you’re missing one of the ends for end-to-end testing.
That’s not Windows’ fault though.&lt;/p&gt;

&lt;h1 id=&quot;documentation-tools&quot;&gt;Documentation tools&lt;/h1&gt;

&lt;p&gt;I think the poor focus of Microsoft on documentation is particularly apparent
here. There is the most perfuctuary support for API documentation. Essentially
you can write arbitrary XML in a comment abover your method, class, etc
definition and have it concatenated together into an XML output file. There’s
no HTML documentation tool built into Visual Studio and the solutions which
exist are clunky at best.&lt;/p&gt;

&lt;p&gt;I suspect that the people at Microsoft think that code-completion is a
substitute for documentation.&lt;/p&gt;

&lt;h1 id=&quot;continuous-integration&quot;&gt;Continuous integration&lt;/h1&gt;

&lt;p&gt;I love &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt;. Having a free service which will
observe a GitHub repository, check out any new commits and then try building
and testing your software in a fresh VM is wonderful. Having had to set up a
Jenkins server in a previous job I cannot tell you how nice it is to have a
system like Travis. It’s brain-dead and simple but, like Unix, that is a
feature in itself. It’s hard to do simple well and Travis does it excellently.&lt;/p&gt;

&lt;p&gt;Travis CI only supports Linux test machines. (Or, at least, the free service
only support Linux.) That’s not going to fly for Windows. Luckily someone
clearly saw a gap in the market and set up
&lt;a href=&quot;http://www.appveyor.com/&quot;&gt;AppVeyor&lt;/a&gt;. It’s not quite as nice as Travis but it’s
also a lot younger. It’s also not &lt;em&gt;quite&lt;/em&gt; as simple to use but it &lt;em&gt;is&lt;/em&gt;
opinionated. That means that you’ll have a nice time if you don’t fight the
“Windows way”. A project which is laid out in the usual Visual Studio way will
Just Build (TM).&lt;/p&gt;

&lt;p&gt;All of my external dependencies are either NuGet-able or are packaged as
re-distributable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.msi&lt;/code&gt; instalers. After some Googling for the correct
PowerShell incantation, getting Bonjour and the Kinect SDKs auto-installing on
the test boxes was easy enough and the NuGet packages are downloaded
automatically by MSBuild.&lt;/p&gt;

&lt;p&gt;I chose to have the non-NuGet dependencies in a &lt;a href=&quot;https://github.com/rjw57/streamkinect2.net-depends&quot;&gt;separate
repository&lt;/a&gt;. Having big
binary blobs in your source repo is a Bad Idea. It’s also a needless waste of
bandwidth for someone who clones your repo when they already have the
appropriate SDKs installed.&lt;/p&gt;

&lt;h1 id=&quot;packaging&quot;&gt;Packaging&lt;/h1&gt;

&lt;p&gt;This is a little frustrating. For some reason, Microsoft saw fit to not ship
the support for packaging projects with the express edition of Visual Studio.
There is only support for publishing a “ClickOnce” package. It’s OK as a
mechanism but I’m reserving judgement until I actually try to deploy this
software onto a separate machine.&lt;/p&gt;

&lt;h1 id=&quot;guis&quot;&gt;GUIs&lt;/h1&gt;

&lt;p&gt;The way that Windows Forms does GUI designing is a little dirty. I prefer a
more explicit separation between UI layout and logic. (WPF seems to be better
in that regard but, from what Googling I performed, it looks like WPF is a bit
of a dead-end for developing GUIs.) As it was, there would be the tiniest of
tiny bits of GUI development for the server. I wanted most of the guts to
live in a library (or “assembly” in Micro-speak) and have the GUI basically be
a start/stop button and a log window. See below:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/streamkinect2-2014-09-17.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m a firm believer in separate modules and composable software and so the GUI
shell is just that: a shell. The &lt;a href=&quot;https://github.com/rjw57/streamkinect2.net/blob/master/StreamKinect2GUI/MainForm.cs&quot;&gt;core logic&lt;/a&gt; is very small.
There’s also a &lt;a href=&quot;https://github.com/rjw57/streamkinect2.net/tree/master/ExampleServer&quot;&gt;console version&lt;/a&gt;
of the server in
the repository which is actually how I run it most of the time. Since there is
so little of it, having the GUI code be a bit ugly isn’t a problem.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;If you are willing to do things the “Windows way” then writing software on
Windows is relatively painless. The packaging and dependency management story
is a lot better nowadays but it still is lacking far behind Linux. Debian has
had &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; for over 15 years now and NuGet is not a lot better than
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; was back then.&lt;/p&gt;

&lt;p&gt;It’s still too hard to set up a project in a way which encourages others to
build it; you need to jump through some hoops to lay it out in a way where your
development machine isn’t special and NuGet is only a partial solution to
dependency hell. It is better than it was though and shows that the Microsoft
ecosystem is becoming a little more friendly to the “Open Source” model of
small projects shared widely.&lt;/p&gt;

&lt;p&gt;Overall, I think this little experiment is a success and I’m well placed to
add actual real Kinect support to my code once the Windows 8 machine arrives.
With the CI system and GitHub, maybe I could even get a student to do it?&lt;/p&gt;

&lt;h1 id=&quot;addendum-the-problem&quot;&gt;Addendum: the problem&lt;/h1&gt;

&lt;p&gt;Below I’ll describe the actual problem I was facing and why I decided to learn
an entirely new platform, new language and new IDE to solve it.&lt;/p&gt;

&lt;p&gt;Firstly, a little context: this year I will have some Masters’ degree students
working on a robotic project which hopes to use a &lt;a href=&quot;http://www.microsoft.com/en-us/kinectforwindows/&quot;&gt;Microsoft Kinect for Windows
version 2&lt;/a&gt; sensor. (This is
the one that comes with the XBox One console.) The project is a research
project and, to make life easier for the students, we will be using the
&lt;a href=&quot;http://www.ros.org/&quot;&gt;ROS&lt;/a&gt; to wire together the various algorithms we will be
using and to control the robot arm.&lt;/p&gt;

&lt;p&gt;When using ROS, the path of least resistance is to use Ubuntu Linux.
While there are some very capable drivers for the original Kinect sensor, there
are unfortunately not currently any drivers for the Kinect version 2 sensor
which I consider stable enough to let students loose on. There is &lt;a href=&quot;https://github.com/OpenKinect/libfreenect2&quot;&gt;a reverse
engineering effort&lt;/a&gt; which may bear
fruit but, as of writing, it is not mature enough for our uses.&lt;/p&gt;

&lt;p&gt;Being a fan of the simplest (a.k.a. “laziest”) solution, I decided that it
would be best to have a dedicated Windows 8 computer with the Kinect plugged
into it. (This is proving a challenge, however, since any spare machines at
work tend to be running Linux. The number of Windows 8 machines is quite
small.) The computer would then stream the depth buffer into the rest of our
Linux-based pipeline. The precise nature of the research project means that
sub-100 millisecond latency isn’t really required.&lt;/p&gt;

&lt;p&gt;Similarly it isn’t a requirement that the depth stream be completely
uncompressed. A quick back-of-envelope calculation reveals that a 1080p60
16-bit depth buffer is around 400 mega&lt;em&gt;bytes&lt;/em&gt; per second. This is far more than
the network at work can deal with. Some brief experimentation showed that one
can non-linearly quantise a depth buffer to 8-bit precision using something
like the &lt;a href=&quot;https://en.wikipedia.org/wiki/%CE%9C-law_algorithm&quot;&gt;μ-law algorithm&lt;/a&gt;
and then compress it with JPEG in real-time to get the required bandwidth down
to a more manageable 1 or 2 megabytes per second.&lt;/p&gt;

&lt;p&gt;All of this experimentation was done in Python and &lt;a href=&quot;https://github.com/rjw57/streamkinect2&quot;&gt;the
code&lt;/a&gt; is available if you want to take
a look. The experimental code also includes a simple client/server pair. The
server advertises itself over
&lt;a href=&quot;https://en.wikipedia.org/wiki/Zero-configuration_networking&quot;&gt;Zeroconf&lt;/a&gt; and
uses &lt;a href=&quot;https://en.wikipedia.org/wiki/%C3%98MQ&quot;&gt;ØMQ&lt;/a&gt; to stream depth frames over
the network. The idea being that we don’t want to spend time configuring IPs,
subnet masks, etc, etc. Ideally we can just plug the Windows 8 machine into the
ROS machine with a network cable and “magic” will happen. Every required
configuration option is another potential point of failure.&lt;/p&gt;

&lt;p&gt;So far, so good. All of the development at this point had been done in Linux
with Python and a little bit of code which mocked up the depth stream I’d be
getting from the Kinect. This allowed some rapid development and the ability to
&lt;a href=&quot;ttps://travis-ci.org/rjw57/streamkinect2&quot;&gt;actually test&lt;/a&gt; the client and server
bits by having the test suite start both on the same machine and provide a
“mock” Kinect device.&lt;/p&gt;

&lt;p&gt;I was vaguely aware that the received opinion is that Python development on
Windows is a little hard but when I finally got around to finding a Windows box
to try installing the software on I found out how bad it was. Firstly, the
“official” Python is almost unusable. For anything other than the bleeding-edge
Python 3.4 release, installing simple tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; is a pain. Getting all
the pieces in line to actually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install&lt;/code&gt; a C-backed module like
&lt;a href=&quot;https://zeromq.github.io/pyzmq/&quot;&gt;pyzmq&lt;/a&gt; is even worse. Continuum Analytics’
&lt;a href=&quot;http://continuum.io/downloads&quot;&gt;Anaconda installer&lt;/a&gt; makes life a lot easier but
it still requires quite an extensive amount of fiddling to get a machine set up
for development.&lt;/p&gt;

&lt;p&gt;This goes against my natural inclination against “special snowflake” machines.
One of the advantages of having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;-like tools in the first place is that
one can use generic VMs in the cloud to build-test your code on each commit.
Then you have an explicit, repeatable and automated recipe for turning your
GitHub repository into installed software. Having to set up a special
development machine will come back to haunt you the first time you need to get
someone else to build (or install) your software.&lt;/p&gt;</content><author><name></name></author><summary type="html">I recently had to do a little bit of Windows development after having managed to avoid it for around seven years. This post documents my experiences from the point of view of an experienced software developer from a non-Windows background.</summary></entry><entry><title type="html">Counting Sneezes from the Web</title><link href="http://www.richwareham.com//counting-sneezes/" rel="alternate" type="text/html" title="Counting Sneezes from the Web" /><published>2014-09-05T00:00:00+00:00</published><updated>2014-09-05T00:00:00+00:00</updated><id>http://www.richwareham.com//counting-sneezes</id><content type="html" xml:base="http://www.richwareham.com//counting-sneezes/">&lt;p class=&quot;preamble&quot;&gt;This post is also available as an IPython notebook which may be
&lt;a href=&quot;/downloads/2014-09-05-counting-sneezes.ipynb&quot;&gt;downloaded&lt;/a&gt;
or &lt;a href=&quot;/downloads/2014-09-05-counting-sneezes.ipynb&quot;&gt;viewed online&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://sneezecount.joyfeed.com/&quot;&gt;sneeze count&lt;/a&gt; website catalogues one
person’s sneezes since 2007. A work colleague asked how difficult it would be to
extract a list of timestamps from the site. Could we then plot sneezes over
time? This post shows how easy this is to do using Python.&lt;/p&gt;

&lt;h1 id=&quot;fetching-the-data&quot;&gt;Fetching the data&lt;/h1&gt;

&lt;p&gt;Conveniently there is an RSS feed available for the site which can be fetched
via Python’s built in &lt;a href=&quot;https://docs.python.org/3/library/urllib.request
.html#urllib.request.urlopen&quot;&gt;urlopen&lt;/a&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.request&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://sneezecount.joyfeed.com/feed'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'utf8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;splitlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
	&amp;lt;item&amp;gt;
		&amp;lt;title&amp;gt;Four thousand and nineteen&amp;lt;/title&amp;gt;
		&amp;lt;link&amp;gt;http://sneezecount.joyfeed.com/four-thousand-and-nineteen/&amp;lt;/link&amp;gt;
		&amp;lt;comments&amp;gt;http://sneezecount.joyfeed.com/four-thousand-and-nineteen/#comments&amp;lt;/comments&amp;gt;
		&amp;lt;pubDate&amp;gt;Fri, 22 Aug 2014 12:41:25 +0000&amp;lt;/pubDate&amp;gt;
		&amp;lt;dc:creator&amp;gt;&amp;lt;![CDATA[Peter]]&amp;gt;&amp;lt;/dc:creator&amp;gt;
				&amp;lt;category&amp;gt;&amp;lt;![CDATA[Sneezes]]&amp;gt;&amp;lt;/category&amp;gt;

		&amp;lt;guid isPermaLink=&quot;false&quot;&amp;gt;http://sneezecount.joyfeed.com/?p=8608&amp;lt;/guid&amp;gt;
		&amp;lt;description&amp;gt;&amp;lt;![CDATA[Seesaw, Twycross Zoo Moderate to strong &amp;amp;#8220;I&amp;amp;#8217;m not heavy enough. And you&amp;amp;#8217;re too heavy.&amp;amp;#8221;]]&amp;gt;&amp;lt;/description&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is also a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paged&lt;/code&gt; query parameter whch lets one get the next page of
results:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.request&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://sneezecount.joyfeed.com/feed?paged=2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'utf8'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;splitlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
	&amp;lt;item&amp;gt;
		&amp;lt;title&amp;gt;Four thousand and nine&amp;lt;/title&amp;gt;
		&amp;lt;link&amp;gt;http://sneezecount.joyfeed.com/four-thousand-and-nine/&amp;lt;/link&amp;gt;
		&amp;lt;comments&amp;gt;http://sneezecount.joyfeed.com/four-thousand-and-nine/#comments&amp;lt;/comments&amp;gt;
		&amp;lt;pubDate&amp;gt;Sat, 09 Aug 2014 17:09:39 +0000&amp;lt;/pubDate&amp;gt;
		&amp;lt;dc:creator&amp;gt;&amp;lt;![CDATA[Peter]]&amp;gt;&amp;lt;/dc:creator&amp;gt;
				&amp;lt;category&amp;gt;&amp;lt;![CDATA[Sneezes]]&amp;gt;&amp;lt;/category&amp;gt;

		&amp;lt;guid isPermaLink=&quot;false&quot;&amp;gt;http://sneezecount.joyfeed.com/?p=8587&amp;lt;/guid&amp;gt;
		&amp;lt;description&amp;gt;&amp;lt;![CDATA[Dining room, Malt Barn, Brize Norton Moderate Accepting the offer of an elderflower Prosecco]]&amp;gt;&amp;lt;/description&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python also has a built in XML parsing module, the
&lt;a href=&quot;https://docs.python.org/3/library/xml.etree.elementtree.html&quot;&gt;ElementTree&lt;/a&gt;
module. Usage is, &lt;em&gt;ahem&lt;/em&gt;, elementary:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;xml.etree.ElementTree&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ET&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_xml_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urlopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getroot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parse_xml_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://sneezecount.joyfeed.com/feed?paged=2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Element 'rss' at 0x7f958d5ccf70&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An RSS feed encodes each post with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item&lt;/code&gt; tag and each publication date
within the item as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pubDate&lt;/code&gt;. A quick and dirty hack to extract all dates
from an RSS feed is simply to list all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pubDate&lt;/code&gt; elements. According to
&lt;a href=&quot;http://codex.wordpress.org/WordPress_Feeds#RSS_feed_
time_and_date_format&quot;&gt;WordPress’s documentation&lt;/a&gt;, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pubDate&lt;/code&gt; tages are RFC822 dates. Python has a
module to deal with that too! The
&lt;a href=&quot;https://docs.python.org/2/library/email.util.html&quot;&gt;email.utils&lt;/a&gt; module has a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parsedate&lt;/code&gt; function. The value can be passed directly to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time.mktime()&lt;/code&gt; to
get a timestamp.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email.utils&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsedate&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mktime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parsedate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'pubDate'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;', '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1407600579.0, 1407520308.0, 1407389858.0, 1407381140.0, 1407344647.0, 1407255800.0, 1407141500.0, 1407069426.0, 1407009164.0, 1406970003.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To get dates for a given feed, therefore, we need simply page through the feed
starting at page 1 until we get a result with no items.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.parse&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urljoin&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.request&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dates_from_feed_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;paged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;all_dates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;paged&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;page_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urljoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'?paged={0}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;dates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_xml_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTPError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Interpret a HTTP error (e.g. 404) as us reaching the end of the list
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;all_dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_dates&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Test with a feed URL for a single month:
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dates_from_feed_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://sneezecount.joyfeed.com/2014/08/feed'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;', '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1408707685.0, 1408628545.0, 1408563641.0, 1408537639.0, 1408439866.0, 1408275986.0, 1408256591.0, 1408167669.0, 1408131465.0, 1407820127.0, 1407600579.0, 1407520308.0, 1407389858.0, 1407381140.0, 1407344647.0, 1407255800.0, 1407141500.0, 1407069426.0, 1407009164.0, 1406970003.0, 1406870597.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can get all dates for all time by using the full feed URL:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dates_from_feed_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'http://sneezecount.joyfeed.com/feed'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we look at the first page of the website, as of writing there are four
thousand and nineteen sneezes. Let’s just ckeck that we’ve got them all:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Fetched {0} sneeze timestamps'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Fetched 4019 sneeze timestamps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;plotting&quot;&gt;Plotting&lt;/h1&gt;

&lt;p&gt;Let’s use matplotlib to plot the timestamps. We first use some IPython magic to
load the pylab environment:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pylab&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rcParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'figure.figsize'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Set the default figure size
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Populating the interactive namespace from numpy and matplotlib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Sneezes over time'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;xlabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Timestamp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ylabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Sneezes'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'on'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-05-counting-sneezes_files/2014-09-05-counting-sneezes_16_0.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Possibly more interesting is a histogram of intervals between sneezes:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Use three hour bins out to 5 days
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestamps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Histogram of times between sneezes'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;xlabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Hours'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ylabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Count'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'on'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;ipynb-output-prompt clearfix&quot;&gt;
  &lt;div class=&quot;pull-left&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
  &lt;div class=&quot;pull-right&quot;&gt;&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2014-09-05-counting-sneezes_files/2014-09-05-counting-sneezes_18_0.png&quot; alt=&quot;png&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;In this post we showed how the Python standard library has all the tools we
require to scrape a website and plot some interesting figures using data from
it.&lt;/p&gt;</content><author><name></name></author><category term="python" /><category term="notebook" /><summary type="html">This post is also available as an IPython notebook which may be downloaded or viewed online.</summary></entry><entry><title type="html">HMAC: How I securely deploy to this site from Travis CI</title><link href="http://www.richwareham.com//hmac-securely-updating-my-website-from-travis/" rel="alternate" type="text/html" title="HMAC: How I securely deploy to this site from Travis CI" /><published>2014-09-02T00:00:00+00:00</published><updated>2014-09-02T00:00:00+00:00</updated><id>http://www.richwareham.com//hmac-securely-updating-my-website-from-travis</id><content type="html" xml:base="http://www.richwareham.com//hmac-securely-updating-my-website-from-travis/">&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;This website has dynamic content like my &lt;a href=&quot;https://www.richwareham.com/links&quot;&gt;link
shortener&lt;/a&gt; and static content like this blog
post. I have Travis CI set up to build my website’s static content whenever a
new commit is made to the github repository. My website has a very simple
method of updating itself: HTTP POST-ing a zip file to a magic URL will unzip
that file over the static file directory. This post is how I try to secure that
mechanism while preserving the simplicity of a “deploy via cURL” model.&lt;/p&gt;

&lt;h1 id=&quot;the-problem&quot;&gt;The problem&lt;/h1&gt;

&lt;p&gt;This website’s source is &lt;a href=&quot;https://github.com/rjw57/richwareham.com&quot;&gt;hosted on
github&lt;/a&gt; although the site itself is
hosted on &lt;a href=&quot;https://www.openshift.com/&quot;&gt;OpenShift&lt;/a&gt;. If you look at the site’s
source code you’ll see that it’s just a pretty simple Python web application
using the &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;flask&lt;/a&gt; web framework for Python.&lt;/p&gt;

&lt;p&gt;Another thing you might notice is that none of the content is in that
repository. That’s because having a full dynamic website for my simple little
corner of the web is overkill. Instead I use &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;jekyll&lt;/a&gt; to
generate a static website from a pile of Markdown files and HTML templates. All
of this magic is hosted in a &lt;a href=&quot;https://github.com/rjw57/richwareham.com-static&quot;&gt;separate
repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why separate the repos like this? Well there is some non-trivial downtime
associated with pushing a new version of an OpenShift webapp. Previously I had
a lot of pre-build and build hook magic which would install jekyll, install
bower, build the static site, etc which was inappropriate for what is,
essentially, a very simple Python webapp.&lt;/p&gt;

&lt;p&gt;By separating out the static content from the dynamic content I can configure
the webapp to simply serve any URL is doesn’t know about from the static
directory. All very nice, clean and modular but how do I update the static
files on my website when I make a change?&lt;/p&gt;

&lt;p&gt;I tend to subscribe to the “Unix laziness” philosophy which holds that spending
some time automating a solution will make things a lot easier in the long run.
The repo holding the static site content is monitored by a &lt;a href=&quot;https://travis-ci.org/rjw57/richwareham.com-static&quot;&gt;corresponding
Travis-CI job&lt;/a&gt;. Travis CI
is very good at installing the various Node.js, Ruby and JavaScript
dependencies to build the site and so we should make use of its strengths.&lt;/p&gt;

&lt;p&gt;So my problem is: I have a webapp serving static content from a directory on
the filesystem and I have Travis with a freshly built pile of HTML, CSS, etc.
How do I get the new content over to the webapp in a secure way? “Secure” here
means that anyone with read access to the webapp repository, static content
repository and Travis CI configuration file shouldn’t be able to scribble all
over my website(!)&lt;/p&gt;

&lt;h1 id=&quot;simple-is-better&quot;&gt;Simple is better&lt;/h1&gt;

&lt;p&gt;I wanted a very simple way to deploy a new bundle of static content. Initially
I thought github releases would be the way forward. My plan was to make Travis
create a new release associated with the static content repo on each
successfully built commit to master. Unfortunately github is geared up to make
releases only when there’s an associated tag. Having to tag each and every
commit seemed ugly and, as usual when things seem ugly, it’s worth taking a
step back and thinking about the problem. A build of the static content isn’t
really a “release” in the github sense; once it’s deployed I don’t need to keep
it around forever more.&lt;/p&gt;

&lt;p&gt;After a bit more thought, I decided to make the deployment process a kind of
“fat webhook”. When Travis built the static content, it would zip it up and
then HTTP POST that file to some URL. (In this case,
https://www.richwareham.com/static-content.) The Python webapp would take the
payload and unzip it into the static files area. Handily Python has a
&lt;a href=&quot;https://docs.python.org/3/library/zipfile.html&quot;&gt;zipfile&lt;/a&gt; module which can
handle zipped up data directly.&lt;/p&gt;

&lt;p&gt;So far, so simple. We only need to write two functions. The first is a utility
function to take a destination directory, ensure it exists and to unzip the
contents of a file object to the directory:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Extracting new static site...'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZipFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'r'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makedirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extractall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The second is a flask handler for the URL:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/static-content'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'POST'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_static_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STATIC_SITE_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'archive'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'OK'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All Travis now needed to do was to zip up the new static site content into a
file called, for example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site.zip&lt;/code&gt; and POST it to my website using
something like cURL:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;archive=@site.zip&quot;&lt;/span&gt; https://www.richwareham.com/static-content
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This works beautifully but it is massively insecure: &lt;strong&gt;anyone can modify my
site’s content&lt;/strong&gt;. To overwrite or create a file on my site an attacker need
only POST a zipfile to a URL which is plainly listed in the Tavis configuration
file.&lt;/p&gt;

&lt;h1 id=&quot;shhh-its-a-shared-secret&quot;&gt;Shhh… It’s a (shared) secret&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Hash-based_message_authentication_code&quot;&gt;HMAC&lt;/a&gt; or
“hash-based message authentication code” is a standard technique to compute a
token given a message and a secret value. Anyone in possession of the same
secret value can then verify that the message is authentic. (Or, at least, was
generated by someone who also knew the secret.)&lt;/p&gt;

&lt;p&gt;In principle it’s quite simple: the deploy script on Travis combines the zip
file and secret together and takes a hash. It then sends the zip file and
computed hash to the webapp. The webapp computes its own version of the hash
from the given zip file and its own copy of the shared secret. If the hashes
match then I know the person who sent me the zip file knows the shared secret.&lt;/p&gt;

&lt;p&gt;HMAC itself is a little more subtle than this but fortunately there is a
&lt;a href=&quot;https://docs.python.org/3/library/hmac.html&quot;&gt;implementation&lt;/a&gt; in the Python
standard library. We’re free to choose our own hash algorithm and so, as a good
default, I chose SHA256.&lt;/p&gt;

&lt;p&gt;The complete Python code to compute a HMAC from an open file object is very small:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hashlib&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;hmac&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;digest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hashlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sha256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hexdigest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# hex-digit string containing HMAC
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the Travis side, this is wrapped up into a small
&lt;a href=&quot;https://github.com/rjw57/richwareham.com-static/blob/master/scripts/calc_hmac.py&quot;&gt;calc_hmac.py&lt;/a&gt;
script. On the webapp side we need only create a check function and modify our
request handler to use it:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;check_hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provided_digest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Compute expected digest
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;digest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileobj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hashlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sha256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Does this digest match? Use compare_digest() because it it constant time.
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# (Timing attacks are subtle. Crypto is hard :(.)
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compare_digest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provided_digest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hexdigest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()):&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/static-content'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'POST'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_static_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Get a file object pointing to the archive sent with the request
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'archive'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Check the HMAC which was provided matches the shared secret
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hmac_ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_hmac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SHARED_SECRET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'hmac'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmac_ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Unzip the static files
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update_static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STATIC_SITE_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'status'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'message'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'OK'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re nearly there. All that’s needed now is a way to get the shared secret to
Travis and the OpenShift webapp in a secure manner.&lt;/p&gt;

&lt;h2 id=&quot;sharing-the-secret&quot;&gt;Sharing the secret&lt;/h2&gt;

&lt;p&gt;Both OpenShift and Travis have the ability to securely store “secret”
information. Travis allows you to store &lt;a href=&quot;http://docs.travis-ci.com/user/encryption-keys/&quot;&gt;encrypted environment
variables&lt;/a&gt; and OpenShift
recently added &lt;a href=&quot;https://www.openshift.com/blogs/taking-advantage-of-environment-variables-in-openshift-php-apps&quot;&gt;secure environment
variable&lt;/a&gt;
support.&lt;/p&gt;

&lt;p&gt;It’s straightforward to arrange for both Travis and OpenShift to have the same
shared secret exposed via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_SITE_SECRET&lt;/code&gt; variable using a little
bit of bash:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dd &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/urandom &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024 | &lt;span class=&quot;nb&quot;&gt;sha1sum&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$OPENSHIFT_APP&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;# cd to checkout of OpenShift app repo&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rhc set-env &lt;span class=&quot;nv&quot;&gt;STATIC_SITE_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SECRET&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$STATIC_SITE&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;# cd to checkout of static site repo&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;travis encrypt &lt;span class=&quot;nt&quot;&gt;--add&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;STATIC_SITE_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SECRET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that this can be automated via a cron job and so the shared secret need
not stay constant. The Python webapp can now just use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os.environ['STATIC_SITE_SECRET']&lt;/code&gt; for the shared secret.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;Above, I outlined the method I use for pushing new static site content to my
website without requiring a time-consuming re-deployment. The astute among you
will notice two potential problems which will need to be considered at a later
date:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The static site contents are just unzipped atop the old ones and so it isn’t
possible to &lt;em&gt;delete&lt;/em&gt; files.&lt;/li&gt;
  &lt;li&gt;Because the zip file is just unzipped over the static files directory, the
update isn’t atomic. A better solution would be to unzip files to a temporary
directory and use some symlink magic to update them atomically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my tiny website neither of these problems are major at the moment although
I may re-visit the solution at a later date.&lt;/p&gt;

&lt;p&gt;Also, crypto is hard. Even though this HMAC based system is about a simple as
it can get, there may still be ways for a sufficiently motivated attacker to
push content to my website. I’m defending against the trivial “LOL his website
accepts any zip thrown at it”-style attacker, not someone willing to put real
effort into defacing my website :). That being said, if you &lt;em&gt;do&lt;/em&gt; find some
obvious way to subvert this method, I’d be interested to hear it. I’m always
willing to learn more about crypto because if anyone claims they know it all,
they’re lying or mad.&lt;/p&gt;</content><author><name></name></author><summary type="html">Overview</summary></entry><entry><title type="html">Can we predict England’s traffic?</title><link href="http://www.richwareham.com//traffic.poster/" rel="alternate" type="text/html" title="Can we predict England’s traffic?" /><published>2014-09-01T00:00:00+00:00</published><updated>2014-09-01T00:00:00+00:00</updated><id>http://www.richwareham.com//traffic.poster</id><content type="html" xml:base="http://www.richwareham.com//traffic.poster/">&lt;p&gt;In September 2014, we presented a poster on some of the ancillary research I am
doing as part of my latest project supported by the &lt;a href=&quot;http://sustainableroadfreight.org&quot;&gt;Centre for Sustainable
Road Freight&lt;/a&gt;. A &lt;a href=&quot;/downloads/srf-meeting-poster-sep-2014.pdf&quot;&gt;PDF of the
poster&lt;/a&gt; is available to download for
those that are interested. The &lt;a href=&quot;https://git.csx.cam.ac.uk/x/eng-sigproc/u/rjw57/srf/docs/poster-sep-2014.git&quot;&gt;source for the
poster&lt;/a&gt;
is hosted on the University git service.&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;The poster focusses on a single part of our wider research project on gathering
traffic flow data. Part A of the project aims to infer traffic flow directly
from video streams but knowing the current state of the world is only one part
of the problem; we are also investigating what techniques can be used to
predict the near future.&lt;/p&gt;

&lt;h1 id=&quot;data-wrangling&quot;&gt;Data wrangling&lt;/h1&gt;

&lt;p&gt;In &lt;a href=&quot;/realtime-traffic-data/&quot;&gt;a previous post&lt;/a&gt; I
outlined some of the basic steps required to get hold of the UK Highways
Agency’s real time data feed and wrangling it in Python. I recommend reading
that post if you’re interested in the details of how the data is fetched and
parsed.&lt;/p&gt;

&lt;h2 id=&quot;what-data-is-available&quot;&gt;What data is available?&lt;/h2&gt;

&lt;p&gt;The UK Highways Agency publish an XML document four times an hour which gives
the latest measurements for stretches of roads (or &lt;em&gt;links&lt;/em&gt;) in England. A link
has a direction; each carriageway of a road will appear as a separate link but
a link may have multiple lanes. This information includes the current traffic
flow (vehicles/hour), mean speed (km/hour) and occupancy (%). An occupancy of
100% means that the road is fully occupied, nose-to-tail, on each lane.&lt;/p&gt;

&lt;h2 id=&quot;how-do-you-get-it&quot;&gt;How do you get it?&lt;/h2&gt;

&lt;p&gt;The XML document itself is available via a public URL which may simply be
fetched. We have developed an automated system based in the Cloud which fetches
this document every 15 minutes, parses the XML and strips the information we
currently do not use. The XML stream itself is 120MB an hour and so this “data
reduction” step is important. We have a full archive of the XML stream for
around six months which is nearly 150GB in size even when compressed.&lt;/p&gt;

&lt;h2 id=&quot;how-do-you-store-it&quot;&gt;How do you store it?&lt;/h2&gt;

&lt;p&gt;Our system in the cloud does two things. Firstly it provides an archive of the
day’s traffic flows, occupancies and speeds which is downloaded and integrated
into a local SQLite database on our site. This database is the source for the
time-series data for individual links. The cloud based system also translate
the current state of England’s traffic into a JSON-formatted document which can
easily be consumed by our web-based visualisation tool (XML, despite its
web-heritage, is rather inconvenient as a file format for today’s web.) On a
more technical note, our cloud service also supports CORS (Cross-Origin
Resource Sharing) which is again a technical requirement for making use of the
data in our visualisation platform.&lt;/p&gt;

&lt;h1 id=&quot;analysis&quot;&gt;Analysis&lt;/h1&gt;

&lt;p&gt;Our central assumption is that all links exhibit similar patterns over the
course of a day. This matches our intuition that we expect morning, evening,
daytime and night time behaviours to be different for any given link but all
links will have some degree of, for example, “morning rush hour”.&lt;/p&gt;

&lt;p&gt;Traditionally this data reduction step would be performed by taking a large
number of “training” samples of daily traffic behaviour and using some
projection-based technique such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Principal_component_analysis&quot;&gt;Principal Component Analysis&lt;/a&gt; to
extract a small set of “basis vectors” or “components” which can be scaled and
summed together to approximate any of the training samples with minimal error.&lt;/p&gt;

&lt;p&gt;These techniques are well known and often very useful as initial data reduction
step. The problem is that the basis vectors produced are not guaranteed to be
positive. Usually this is not a problem but in traffic analysis one of the few
things we can say with certainty is that speeds, occupancies and traffic flows
are positive.&lt;/p&gt;

&lt;p&gt;Taking a naïve PCA of 40,000 traffic flow daily samples results in these
components:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/pca-traffic-flow.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The only directly interpretable component is the first cone which corresponds
to the normalised mean of all traffic flows. The rest are not very
illuminating. In the case of traffic analysis one has a lot of prior knowledge
available which does not obviously map onto these components.&lt;/p&gt;

&lt;p&gt;Instead of PCA, we can make use of a relatively novel technique: &lt;a href=&quot;https://en.wikipedia.org/wiki/Non-negative_matrix_factorization#Others&quot;&gt;non-negative
matrix factorisation&lt;/a&gt;. In this technique one forces all of the components
to be positive. The downside is that the factorisation is no-longer unique. To
combat this, one usually factorises while minimising some “sparseness measure”.
In our case we factorise while trying to keep the components as “peak-like” as
possible. Fortunately the NMF implementation we are using, &lt;a href=&quot;http://scikit-learn.org/&quot;&gt;scikit-learn&lt;/a&gt;,
directly supports this. The non-negative fundamental components look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/nmf-traffic-flow.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It is now far easier to directly interpret these components in terms of
“morning rush hour”, “night time flow”, etc.&lt;/p&gt;

&lt;p&gt;By resolving our training flows onto these components we end up with 5
components which describe the flow on one link over an entire day rather than
one number for each quarter hour sample. This is around a 5% data reduction.&lt;/p&gt;

&lt;p&gt;We may now reason using these components to derive “typical” component values
for each flow for each weekday. Typical results from this projection are shown
in the poster.&lt;/p&gt;

&lt;h1 id=&quot;prediction&quot;&gt;Prediction&lt;/h1&gt;

&lt;p&gt;Data reduction is all well and good but it is all for naught if the reduced
data doesn’t represent the actual data. To this end we investigated how well
our “typical week” will predict a new week which wasn’t present in our training
data. To add some challenge, the new week included a Bank Holiday Monday which
wasn’t present in the training set.&lt;/p&gt;

&lt;p&gt;Reconstructing our “typical week” for each link resulted in surprisingly good
performance. Remember that this is the simplest form of prediction; we assume
that each week is like the typical week. Even with such a simple model our
prediction has a median relative error of only 7%. Plotting a scatter chart of
predicted flow versus actual flow showed a tight adherence to the perfect 1:1
correspondence line. The bank holiday was clearly visible in the predicted
output.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;We investigated whether non-negative matrix factorisation of traffic flows in
England could provide large scale data reduction with low error while providing
basis components which retained interpretable. We showed that even reducing
data to 0.25% of the original we could predict future flows with a median
relative error of 7%.&lt;/p&gt;

&lt;p&gt;Given these results we intend to proceed with traffic prediction working
directly on the reduced traffic data which we believe to be a more tractable
problem than dealing with the full dataset directly.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Non-negative_matrix_factorization#Others&quot;&gt;Non-negative Matrix Factorization (Wikipedia references)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Principal_component_analysis&quot;&gt;Principal Component Analysis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://scikit-learn.org/&quot;&gt;Scikit-learn&lt;/a&gt;: a Python package for machine learning&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">In September 2014, we presented a poster on some of the ancillary research I am doing as part of my latest project supported by the Centre for Sustainable Road Freight. A PDF of the poster is available to download for those that are interested. The source for the poster is hosted on the University git service.</summary></entry><entry><title type="html">Meet Emma, our latest robot</title><link href="http://www.richwareham.com//meet-emma/" rel="alternate" type="text/html" title="Meet Emma, our latest robot" /><published>2014-08-01T00:00:00+00:00</published><updated>2014-08-01T00:00:00+00:00</updated><id>http://www.richwareham.com//meet-emma</id><content type="html" xml:base="http://www.richwareham.com//meet-emma/">&lt;p&gt;Our lab has a new member. For next year’s Masters projects, we are partnering
with a local tech consultancy to develop a robotic project. We’ll be using the
&lt;a href=&quot;http://crustcrawler.com/products/AX-18F%20Smart%20Robotic%20Arm/&quot;&gt;CrustCrawler AX-18A Smart Robotic
Arm&lt;/a&gt; in the
project. Ultimately we’re going to have four: one per student. Before that,
though, we need to make sure that we know what bits to get. She took quite a
while to put together but now Emma lives!&lt;/p&gt;

&lt;p&gt;In case you’re wondering, one possible project aim is to peel a banana with a
scalpel. The arm is, somewhat playfully, named after &lt;a href=&quot;https://en.wikipedia.org/wiki/Emma_Peel&quot;&gt;Mrs Emma
Peel&lt;/a&gt; from &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_Avengers_(TV_series)&quot;&gt;The
Avengers&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</content><author><name></name></author><summary type="html">Our lab has a new member. For next year’s Masters projects, we are partnering with a local tech consultancy to develop a robotic project. We’ll be using the CrustCrawler AX-18A Smart Robotic Arm in the project. Ultimately we’re going to have four: one per student. Before that, though, we need to make sure that we know what bits to get. She took quite a while to put together but now Emma lives!</summary></entry></feed>