I see, so the camera sensor area covered by moving your head in-place to point to the four corners of the display is approximately 90x30.<br><br>I think you are saying that the image of the reflective dot is a blob of neighboring pixels of size 5x3 to 20x12 somewhere on the display.  If that is the case, then taking the centroid is probably the best approach.  However I may make a suggestion to improve accuracy:
<br><br>-- Threshold image at some minimum brightness<br>-- Find largest blob of connected pixels above threshold, and put them into a set B<br>-- Put all pixels just inside or just outside the edge of the blob into a set E
<br>-- Find all internal pixels I = B \ E  (all pixels in B that are not in E)<br>-- Linearly scale the (floating point) intensity of all pixels in E so the brightest is 1.0 and the darkest is 0.0.  (Beware of division by zero if all edge pixels are the same intensity.)
<br>-- Set the intensity of all pixels in I to 1.0<br>-- Find the weighted centroid of (I Union E), by multiplying the pixel intensity by the x and y coords, and summing for x and y, then dividing x and y sums by the total of all pixel intensities.
<br><br>What this effectively does is give all internal pixels equal weight in calculating centroid position, and then it uses the normalized shade of edge pixels to fractionally adjust the centroid position.  This effectively reverses the antialiasing that occurs at the edge of the image of the dot on the CCD.  You really should be able to get some good resolution using some sort of approach like this.
<br><br>By the way, as someone pointed out in another post, trying to hold your head in position and just tilt it slightly to reach the corners of the laptop is likely to give you a knot in your neck!  You should probably allow the user to move their head further (because their effective range is extended by their eyes).  You might also want to consider doing some eye tracking, so that the user can blink twice to turn the head tracking on or off, for example, and maybe wink to trigger a L/R mouse button action.
<br><br>Also you should look at relative positioning and not just absolute positioning: e.g. the cursor doesn't move unless you move your head quickly, and the movement is only part of the width of the screen each time -- that way you can move the cursor in several steps, by moving quickly forward in one direction, then slowly back, much the same way as you do with a touch pad, except that touching/raising your finger is emulated by the speed threshold.
<br><br>Luke<br><br><br><br><br><div><span class="gmail_quote">On 9/5/07, <b class="gmail_sendername"><a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de</a></b> <<a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de
</a>> wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Hello Luke,<br><br>I will consider the paraboloid thing, thanks for the code.
<br><br>I am not tracking only one point. I am tracking a bundle of points - say 5x3 to 20x12 - and it changes all the time with different ratio. Out of those pixels (and maybe some time) I have to build an AVG Pixel with a defined XY value. AVG pixel != brightest pixel. For that AVG pixel I could use subpixel, too. I have to use subpixel because I have to map the low resolution to 1200x900 pixel if I try to use absolute coordinates.
<br>Where do I get the size 90x30 from? It is not an pixel exact value but a approximation. It differs.<br>I tried not to move my eyes but my head and looking to the four corners and the center of the XO display. The display is very small so I moved my head really not to much. One infrared filtered result merged from 5 snapshots (four eges and center) is here:
<br><a href="http://www.olpcaustria.org/mediawiki/upload/7/79/Headtracker_area_small.jpg">http://www.olpcaustria.org/mediawiki/upload/7/79/Headtracker_area_small.jpg</a><br>It is downsized from 640x480 to 320x240px but the relation is the same.
<br><br>Maybe I have got a knot in my brain. I really like to get the headtracker working in a good way without an additional lens.<br>Again the link to the project site:<br><a href="http://www.olpcaustria.org/mediawiki/index.php/Headtracker">
http://www.olpcaustria.org/mediawiki/index.php/Headtracker</a><br><br>greeting,<br>yokoy<br><br><br><br><br>On Wed, 5 Sep 2007 12:57:54 -0400<br>"Luke Hutchison" <<a href="mailto:luke.hutch@gmail.com">luke.hutch@gmail.com
</a>> wrote:<br><br>> PS there's a "cheap" way you can accomplish subpixel accuracy, as follows.<br>> You basically take a bunch of 1D samples through the brightest pixel,<br>> looking at 2 neighbors in each direction, and then take a weighted sum of
<br>> the results.  This calls the code I pasted in the last email.  It's not<br>> going to be as good as paraboloid regression, but it should allow you to<br>> test feasibility.<br>><br>><br>> // Do a 2D parabolic fit to give sub-pixel accuracy for translational
<br>> // registration, by performing four 1D parabolic fits through the closest<br>> // integer correlation offset: horizontally, vertically, and on the leading<br>> // and trailing diagonals.  Take the weighted centroid of all four to get
<br>> // the true correlation offset.<br>><br>> // off_x and off_y are the one-pixel-accurate correlation offsets recovered<br>> // by correlation.<br>><br>> // x1d and y1d are x and y values for the 1D quadratic fit function
<br>> double y1d[9] = {     // Get magnitudes at (off_x,off_y) and 8 neighbors<br>>   dft_mag(dft, off_x - 1, off_y - 1),<br>>   dft_mag(dft, off_x    , off_y - 1),<br>>   dft_mag(dft, off_x + 1, off_y - 1),<br>
>   dft_mag(dft, off_x - 1, off_y    ),<br>>   dft_mag(dft, off_x    , off_y    ),<br>>   dft_mag(dft, off_x + 1, off_y    ),<br>>   dft_mag(dft, off_x - 1, off_y + 1),<br>>   dft_mag(dft, off_x    , off_y + 1),
<br>>   dft_mag(dft, off_x + 1, off_y + 1)<br>> }<br>><br>> // Sum contributions to centroid of each quadratic fit<br>> double x1d_tot = 0.0, y1d_tot = 0.0, x1d;<br>><br>> // Parabolic fit in horiz direction through correlation maximum
<br>> x1d = parabolic_fit(-1, y1d[3], 0, y1d[4], 1, y1d[5]);<br>> x1d_tot += x1d;<br>><br>> // Parabolic fit in horiz direction through correlation maximum<br>> x1d = parabolic_fit(-1, y1d[1], 0, y1d[4], 1, y1d[7]);
<br>> y1d_tot += x1d;   // [x1d is x in parabola space, but y in correlation<br>> space]<br>><br>> // Weight contributions of diagonal by the inverse of their distance<br>> #define RT2_OV_2  0.7071067811865475244
   // sqrt(2)/2  (= 1/sqrt(2))<br>><br>> // Parabolic fit in leading diagonal direction through correlation maximum<br>> x1d = parabolic_fit(-1, y1d[0], 0, y1d[4], 1, y1d[8]);<br>> x1d_tot += x1d * RT2_OV_2;<br>
> y1d_tot += x1d * RT2_OV_2;<br>><br>> // Parabolic fit in leading diagonal direction through correlation maximum<br>> x1d = parabolic_fit(-1, y1d[2], 0, y1d[4], 1, y1d[6]);<br>> x1d_tot -= x1d * RT2_OV_2;<br>
> y1d_tot += x1d * RT2_OV_2;<br>><br>> // Take centroid of all parabolic fits, weighting diagonals by RT2_OV_2;<br>> // make relative to correlation coords by adding off_x, off_y<br>> double subpix_off_x = off_x + x1d_tot / (
2.0 + 2.0 * RT2_OV_2);<br>> double subpix_off_y = off_y + y1d_tot / (2.0 + 2.0 * RT2_OV_2);<br>><br>><br>><br>> On 9/5/07, Luke Hutchison <<a href="mailto:luke.hutch@gmail.com">luke.hutch@gmail.com</a>> wrote:
<br>> ><br>> > Where do you get the size 90x30 from though?  Are you saying you can't get<br>> > at the full-sized frame through the API currently?<br>> ><br>> > You really should consider fitting a paraboloid over the dot to get
<br>> > sub-pixel resolution.  Note that if the dot is bigger (more than a few<br>> > pixels), you probably want to just use the weighted centroid, but if it's<br>> > small, a paraboloid is the right approach.  You really will get at least a
<br>> > 10x increase in accuracy in both x and y, bringing your effective resolution<br>> > to something like 900x300 for the example you gave.  You may not even need a<br>> > lens.  I have used this before with success for an image processing project.
<br>> ><br>> ><br>> > Here's the code for the 1D version:<br>> ><br>> > // Fit a parabola to three points, and return the x coord of the turning<br>> > // point (point 2 is the central point, points 1 and 3 are its neighbors)
<br>> > double parabolic_fit(double x1, double y1,<br>> >                      double x2, double y2,<br>> >                      double x3, double y3) {<br>> ><br>> >   double a = (y3 - y2) / ((x3 - x2) * (x3 - x1)) -
<br>> >              (y1 - y2) / ((x1 - x2) * (x3 - x1));<br>> ><br>> >   double b = (y1 - y2 + a * (x2 * x2 - x1 * x1)) / (x1 - x2);<br>> ><br>> >   double xmin = x2;       // Just use central point if parabola is flat
<br>> >   if (fabs(a) > EPSILON)<br>> >     xmin = -b / (2 * a);  // [avoids division by zero]<br>> ><br>> >   // Use the following to calculate the y-value at the turning point<br>> >   // of the parabola:
<br>> >   //<br>> >   //   double c = y1 - a * x1 * x1 - b * x1;<br>> >   //   double ymin = a * xmin * xmin + b * xmin + c;<br>> ><br>> >   return xmin;<br>> > }<br>> ><br>> > I don't have code for the 2D version unfortunately.
<br>> ><br>> > The 2D version (fitting a paraboloid to a neighborhood of more than four<br>> > points total) is overspecified, as is the 1D version if fitting a parabola<br>> > to more than three points (
e.g. using two neighbors on either side of the<br>> > brightest pixel).  Thus you need to do some sort of regression to find the<br>> > best fit.  I'm sure there is code out there to accomplish this.<br>> >
<br>> > Luke<br>> ><br>> ><br>> > On 9/5/07, <a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de</a> <<a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de</a>> wrote:<br>> > ><br>
> > > Hello Luke,<br>> > ><br>> > > On Tue, 4 Sep 2007 16:11:34 -0700<br>> > > "Luke Hutchison" < <a href="mailto:luke.hutch@gmail.com">luke.hutch@gmail.com</a>> wrote:<br>
> > ><br>> > > > Is the processing time for 640x480 the reason you're only using 90x30?<br>> > > ><br>> > ><br>> > > No, I am not at the point thinking about optimization the processing
<br>> > > time.<br>> > > You don't want to dance in front of the camera to control the mouse<br>> > > pointer. You just want to move your head a few degrees as if you would look<br>> > > to the edges of the display without moving your eyes. Then the area
<br>> > > recognized from the camera is very small. It is like moving the mouse only<br>> > > one or two millimeters to move the mousepointer over the whole desktop. To<br>> > > get more 'delta pixel' I need a mag lens, I think.
<br>> > ><br>> > > regards,<br>> > ><br>> > > yokoy<br>> > ><br>> > ><br>> > ><br>> > > > You can actually dramatically increase the precision to which you can
<br>> > > read<br>> > > > back the bright point's location by fitting a paraboloid to the<br>> > > intensity<br>> > > > values in the neighborhood of the brightest pixel, then reading off
<br>> > > the<br>> > > > location of the extremum of the paraboloid.  You will get at least one<br>> > > order<br>> > > > of magnitude more accuracy that way than looking at the integer coords
<br>> > > of<br>> > > > the brightest pixel (perhaps as much as two orders of magnitude).<br>> > > ><br>> > > > Luke<br>> > > ><br>> > > ><br>> > > > On 9/4/07, 
<a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de</a> <<a href="mailto:linaccess@yokoy.de">linaccess@yokoy.de</a>> wrote:<br>> > > > ><br>> > > > > Hello Mary Lou,<br>> > > > >
<br>> > > > > On Tue, 04 Sep 2007 12:13:34 -0400<br>> > > > > Mary Lou Jepsen <<a href="mailto:mljatolpc@gmail.com">mljatolpc@gmail.com</a>> wrote:<br>> > > > ><br>> > > > > > lenses are cheap.  it depends on what exactly you are doing with
<br>> > > the<br>> > > > > > software.<br>> > > > ><br>> > > > > tracking a little shiny point at the head and transform it into<br>> > > > > mousepointer movements. Here is the description:
<br>> > > > > <a href="http://www.olpcaustria.org/mediawiki/index.php/Headtracker">http://www.olpcaustria.org/mediawiki/index.php/Headtracker</a><br>> > > > ><br>> > > > > With the XO camera we typicaly use only 90x30 pixel from the 640x480
<br>> > > > > pixel. So I want to magnify the operative area with a lens.<br>> > > > > Here is a picture of the area:<br>> > > > ><br>> > > > > <a href="http://www.olpcaustria.org/mediawiki/index.php/Headtracker#magnification_lens">
http://www.olpcaustria.org/mediawiki/index.php/Headtracker#magnification_lens</a><br>> > ><br>> > > > ><br>> > > > ><br>> > > > ><br>> > > > > > American Science and Surplus is a good way to experiment:
<br>> > > > > > <a href="http://sciplus.com/category.cfm?subsection=21">http://sciplus.com/category.cfm?subsection=21</a><br>> > > > > ><br>> > > > ><br>> > > > > thank you for that link. A plastic lens is what I am searching for.
<br>> > > > ><br>> > > > ><br>> > > > > > then to china for mass production at very low price point.<br>> > > > > ><br>> > > > > ><br>> > > > > > - Mary Lou
<br>> > > > > ><br>> > > > > ><br>> > > > ><br>> > > > > regards,<br>> > > > ><br>> > > > > yokoy<br>> > > > > --
<br>> > > > ><br>> > > > > _______________________________________________<br>> > > > > Devel mailing list<br>> > > > > <a href="mailto:Devel@lists.laptop.org">Devel@lists.laptop.org
</a><br>> > > > > <a href="http://lists.laptop.org/listinfo/devel">http://lists.laptop.org/listinfo/devel</a><br>> > > > ><br>> > > ><br>> > ><br>> > ><br>> > > --
<br>> > ><br>> > ><br>> ><br>><br><br><br>--<br><br>_______________________________________________<br>Devel mailing list<br><a href="mailto:Devel@lists.laptop.org">Devel@lists.laptop.org</a><br>
<a href="http://lists.laptop.org/listinfo/devel">http://lists.laptop.org/listinfo/devel</a><br></blockquote></div><br>