Finding a Laser Dot

From LadypackWiki

The tour guide uses a laser pointer to gesture at and indicate objects of interest. This page describes a method to determine where the tour guide is pointing. To make this easier, the tour guide actually uses two co-aligned laser pointers, one red and one green.

Table of contents

Overview

There are 5 steps to this process

  1. convert to HSV color space
  2. score each pixel by its "redness" and its "greenness"
  3. run a point detector to search for points of redness and greenness
  4. use a low-pass filter to account for spurious pixels and chromatic aberration
  5. find the reddest and greenest points that are closest together.

Here is a sample image with the two laser dots on the door to Russ's office.

Image:findredgreenlaser-orig.jpg

Redness and Greenness

In HSV color space, with values ranging from 0 ... 1, a pure red pixel has value (0.0, 1.0, 1.0) and a pure green pixel has value (0.333333, 1.0, 1.0)

I found a guassian weighting function to be a very good indicator of the redness or greenness of a pixel. If a pixel P is the three-vector (h, s, v), then:

Redness( P ) = e^{ - \frac{1}{2} (P - \mu_r)^T\Sigma_r^-1(P- \mu_r) }

where I found μr = (0.0,1.0,1.0)T and Σr = diag(0.05,0.22,0.22) to work well.

Since I'm using a diagonal covariance matrix Sigmar, the above equation is equivalent to:

Redness( P ) = e^{ -(h-\mu_h)^2 / ( 2 \sigma_h^2 ) }e^{ -(s-\mu_s)^2 / ( 2 \sigma_s^2) } e^{- ( v-\mu_v)^2 / ( 2 \sigma_v^2 ) }

Similarly,

Greenness( P ) = e^{ - \frac{1}{2} (P - \mu_g)^T\Sigma_g^-1(P- \mu_g) }

and μg = (0.333333,1.0,1.0)T and Σg = diag(0.12,0.5,0.63) worked well for the green laser.

Note that there's no Gaussian scaling factor, as the point is to have the "redness" and "greenness" functions range from 0 ... 1

Redness of the above image:

Image:findredgreenlaser-red.jpg

Greenness of the above image:

Image:findredgreenlaser-green.jpg

Point detection

To locate blobs and points, Edward Rosten's FAST corner detector (http://mi.eng.cam.ac.uk/~er258/work/fast.html) works very well. Running the corner detector on the redness and greenness images gives:

Image:findredgreenlaser-redpoints.jpg

Image:findredgreenlaser-greenpoints.jpg

Lowpass filtering

Now we have a list of red and green points. A number of these are likely due to chromatic aberration, so we must discard them as soon as possible. Consider the following image:

Image:fishtank-chromatic-aberration-full.jpg

Zooming in on the outlined region, you can see quite a few spurious (highly saturated) red and green pixels due to chromatic aberration and the Bayer tiling of the CCD.

Image:fishtank-chromatic-aberration-closeup.png

These deviations tend to be limited to inidivudal pixels, so a low pass filter will smooth them away nicely. I'm using the dead simple filter:

\begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1  \end{bmatrix}

Running the filter on an entire image can be time consuming, but we only care about a handful of points, so we can selectively filter those points and keep the computation time down.

Proximity weighting

Now we have a list of red points and a list of green points, and we want to pick the red and green point that are most likely to be generated by the lasers. Each pair of red and green points R and P are scored according to the following metric:

Strength( R, G ) = \frac{ filtered\_redness( R ) filtered\_greenness( G ) }{ min( 10, dist( R, G ) ) }

where dist(R,G) is the Euclidean distance between R and G. The min() function encodes the notion that proximity is only valuable up to point. If anything, pairs closer than 10 pixels are less likely to be the laser dots, due to the way the lasers are arranged and the likely distance the dots will be.

The pair with the highest strength is then determined to be the two laser dots.

Final result, with red and green dots circled, and a larger circle centered on the midpoint between the two dots.

Image:findredgreenlaser-final.jpg

Navigation