--- layout: post title: "Curves and Beer: The Hough Transform" categories: [Hacking] tags: [Featured, Software, _Phase4] ---
Lines from Pixels
_Part of a brief series that started [here.]({{ site.baseurl}}{% post_url 2018-12-05-K-Means %})_ {: .notice--info} The world is full of patterns: art, physics, weather, music. In some camps, it's been proposed that human ability to grasp at patterns is the core of conscious experience. We understand our surroundings, and one another, as a complex carpet of overlapping and expected patterns. The patterns might be hard to fathom at first, then... like the sudden twist in a mystery story, _Aha!_ the experience of understanding. It all makes sense now. With that possibility for epiphany in mind, here's a paradoxical pattern to consider: _when is a point a line, and when is line a point, especially when that line is.. a curve? And how, may I ask, does this relate to beer?_ May we present: _The Hough Transform..._ ### The Idea The story of the Hough Transform, like many great ideas, begins with our last question above: _the beer._ #### Bubbles In 1952 physicist [Donald A. Glaser](https://mcb.berkeley.edu/news-and-events/department-news/glaser-bubble-chamber) invented the ["bubble chamber,"](https://en.wikipedia.org/wiki/Bubble_chamber) a device for tracing the motions of charged particles moving through a liquid. [_Some of the initial experiments used beer inside the chamber._](https://www2.lbl.gov/Publications/Currents/Archive/Jul-21-2006.html#6) Of course they did, this was at a university. But the only reported results from the high-energy beer physics experiments, alas, were some bad smells (seriously). Switching from beer to liquid hydrogen led to Glaser winning the Nobel Prize only eight years later, in 1960. The year before that prize, 1959, another researcher, [Paul Hough,](https://www.gf.org/fellows/all-fellows/paul-v-c-hough/) published "Machine Analysis of Bubble Chamber Pictures," in the popular _Proceedings of the International Conference on High Energy Accelerators and Instrumentation._ If you missed that, he also filed a [1962 patent.](https://patents.google.com/patent/US3069654A/en) The problem Hough was trying to get at was this: how to find straight lines in very low-quality, sketchy, incomplete pictures? Including ones where there's a lot of extra noise from spurious bubbles, low-quality signals (this is 1959, using a 1950's-era TV camera), and how to do it _quickly_ (at TV camera speed)?
Fermilab Bubble Chamber Image
Not a Hokusai or da Vinci sketch, but a bubble chamber photo from Fermilab
Breaking down our perceptions into patterns is a big part of what has been termed "artificial intelligence" at least as far back as those days in the 1950's. Hough, or you or I, could look at a picture with broken lines and instantly understand how the pieces connect into a single continuous stroke. But how might this perception be achieved automatically? #### Lines Can Be Points Hough had the genius idea to think of _Lines as Points._ Pictures like those on a TV camera or a computer display are made from points in X and Y dimensions: pixels, arranged across the width and height of the picture. Just like Mr. Evan's Anaylytic Geometry class back in Junior High. As Mr. Evans probably told you, in algebra any perfect, infinite line on an XY picture can be described by a little equation that you might recall from that classroom: _y = m⋅x + b_ where _m_ is the slope (how steep is the line?), and _b_ is the "y-intercept" where the line crosses the x=0 axis (that is: changing _b_ moves the line up and down on the chart). Since a perfect infinite line doesn't care about the _specific_ x or y, we can just express it as _m_ and _b_ — which in turn could just be a single point _(m,b)_ on its own 2D graph, where, say, the horizontal axis is slope _m_ and the vertical one is _b._ Which is exactly what Hough did. A problem with this description is that vertical lines, where the slope is infinite, can't be drawn. This worked out okay for Hough's specific case. In fact it worked _great_ for him. But it's kind of bad if we want to use this to look at most everyday pictures, which have lots of vertical lines relating to buildings, trees, and so forth. A decade later, two researchers at Stanford, [Duda & Hart,](http://www.ai.sri.com/pubs/files/tn036-duda71.pdf) realized you could just use a different equation for straight lines to fix that problem. The equation they prefered uses an angle between zero and 180 degrees to define the line's _tilt_ (called _theta_ or θ),and the _radial distance_ of the line from the (0,0) origin (called _rho_ or ρ). The ρ distance for lines that appear in any real picture can never be farther than the distance between a picture's opposite corners. The illustration below shows the relationships. Any line that can appear in the dark image box on the right is also represented by the single highlighted point in the white rectangle of (θ, ρ) space on the left. You should be able to see how changing values of tilt and distance affect where the picture line is drawn. The specific line equation for theta and rho, which you're not at all required to recall later, is: _x⋅cosθ + y⋅sinθ = ρ_
Similarly, any point on the θ/ρ plane represents line in the image plane.
#### Points and Lines to Curves Okay, we have some interesting ways to write about straight lines, but how do we go from a _picture,_ made up of many single points, to finding meaningful straight lines that join those points? Especially since we don't know which points are "good" important points, and which ones are just other random parts of the picture? First, Hough knew that the lines he cared about were along _edges_ in the picture. In the bubble chamber, a picture was contrasty black-and-white, like in the photo above (but lower quality). So he just watched for sudden changes in brightness along each tiny part of the TV signal, and only paid attention to places where the values jumped between light and dark or back. In our sample code, we use a pretty similar edge finder called the _Sobel filter,_ likewise looks for changes in neighboring pixel brightnesses and mixes edges found in both vertical and horizontal directions (we can talk about the specifics of Sobel in another post).
Color image, grayscale, vertical/horizontal edges (marked in color)
If a pixel isn't on an edge, we can ignore it -- a big time saver for some pictures. So now that we've picked some points (pixels) as best candidates to be part of a line, let's consider just one pixel's worth: one isolated point. There's an infinite number of _possible_ lines that could pass through that point, with tilts ranging around a full 180 degrees. Lines tilting up, tilting sideways, anywhere between. If we look at it in our θ/ρ space, we can plot a graph of all the possible values of the tilts and corresponding distances for the possible lines that pass through that point, for every angle from -90 degrees to + 90 degrees. The resulting graph shape will show a smooth, sinuous curve of different possible distances, one for each angle we consider. Time for another Hough insight. #### Curves to Lines Let's now consider a second point on the same picture. It too has an infinite number of possible lines in the picture that could cross it, but only _one_ of those lines will cross _both_ the first and second points. If we add to our graph the same sort of curve described above for that new point, the two curves of possibilities will always cross one another, exactly _once,_ somewhere on our graph of θ/ρ space. And where the curves cross is _exactly the point that describes the line between those two original picture points._ _Mind blown._ (ymmv)
For any pixel on the image plane, there will be infinitely many possible combinations of θ (horizontal) and ρ (vertical) that describe lines that pass through that point. Here we show the possible values between -90 and 90 degrees of θ, and where the two possiblities coincide — which will be at the θ/ρ representation of the straight image-space line connecting the two points.
#### Curves to Lines: Taking It to the Voters You might be asking: _Couldn't we have found a line between two points in a much, much, easier way?_ Sure, for just two points. But what about for _lots_ of points? Let's add a third point. It might not be along the same connecting line as those first two, but if it is, we have that much more reason to think that our line is a good one. Its curve will intersect in just the same spot. The more points that line up, the stronger our conviction that this particular combination of theta and rho is one we like. We can keep doing this for every candidate point. So let's let every pixel vote! To allow voting, we can just accumulate all the curves on one gray-scale graph of pixels, adding up the intersections for all possible tilt angles and for all the edge points in the picture. The brightest spot(s) in the graph will correspond to the strongest lines in the original picture. In our sample, we've slowed the process down a bit for the benefit of human viewing -- in practice it's very quick, and even quicker if you can exploit a GPU (as is done, say, in the cameras of a self-driving car, but not done here).
Scanning through each row, we find more and more curves that overlap. The brightest areas are those with the largest number of overlaps. On the original picture, we overlay the lines from the strongest values found-so-far as we scan from top to bottom.
There you have it! Hough's brilliant idea. * Lines can be points * An infinite collection of possible lines can be a line (of lines) * ...or a curve (of lines) * When drawn in a collection of pixel points, curves made from points can vote for the best lines that might connect those points Since 1959 Hough's gymnastic transform been extended for many other uses and different sorts of shapes. His paper is one of the most-cited in all of computer vision. I have no idea if it's been used to trace actual beer bubbles. Maybe someone should try, the effort could at least be entertaining while the test supplies last. --- ### The Example Code As usual, all code here is contained within the HTML — no calling-out to external resources like _d3_ etc. Selecting "view source" or looking at [this github page](https://raw.githubusercontent.com/joker-b/botzo/master/_posts/2018-12-17-Hough.md) will reveal all. The single `