Monday, June 9, 2008

ISight Screen Saver

A while ago I wrote a screen saver for OSX as a learning exercise. I wanted to learn how to access the camera built into my Macbook Pro and get my feet wet with OSX. I was going to integrate face recognition using the OpenCV or MPT libraries but never got around to it. I'm posting it now because some of the guys at work saw it and wanted a copy. I added the source to my SVN repository and made a binary available via the download site. The screen saver is really as minimalistic as it gets. It uses the new QTKit API for accessing the camera and sets the capture view to the frame size of the inherited ScreenSaverVeiw class. The QTKit API came out around the time of Leopard's release and is much simpler and has better performance then the old streamgrabber API which had been around since the OS 9 days. To install the binary just download the zip file, unzip and double click. It will automatically install itself and become available as a screen saver in the system preferences.

Sunday, June 8, 2008

Jarvis March Convex Hull Algorithm in F#

I recently obtained the "Dutch" computational geometry book to supplement the other computational geometry book that I have. Instead of doing what I normally do which is skip directly to the topic that I'm interested in, I've resolved to work through the entire book systematically implementing each of the algorithms in F# or some other functional language. The purpose is two fold; I know almost nothing about the topic and it's hard to find anything about computational geometry implementations in a functional language. Who knows, someone may actually find my floundering helpful.

Chapter one is all about convex hulls and after doing a little bit of reading I determined that the "Jarvis March" algorithm looked like a good place to start. I've only included core part of the algorithm here in the blog post. The entire source including the visualization can be found here.

The algorithm is pretty simple I start first by sorting the point list by the Y coordinate and use the minimum as the starting point for my hull. I start working through the list trying to find the left most point in relation to the previous point added to the hull; this is the fold_left bit of the _jarvis function. Once I've found the left most point I append it to the head of the hull list and make a recursive call to _jarvis. When I reach the point with the maximum Y value I know that I have successfully built the left side of the hull. I then repeat the entire process but this time instead of searching for the left most point I search for the right most point building up the right side of the hull. When done I append these two lists together to form the complete hull.

type Point = {
   x :double;
   y :double;
   } with
  
   static member ( = ) a b =
       a.x = b.x && a.y = b.y;
    
   static member ( <> ) a b =
       not(a = b);
  
end;;
  

type Edge = {
   orig:Point;
   dest:Point;
   } with

   static member ( = ) a b =
       a.orig = b.orig && a.dest = b.dest;
    
   static member ( <> ) a b =
       not(a = b);
      
end;;
  

let sort point_list =
   List.sort( fun p1 p2 ->
              match Pervasives.compare p1.y p2.y with
              | 0 -> Pervasives.compare p1.x p2.x;
              | _ as v -> v;
             
   ) point_list;;


//    Input:  three points P0, P1, and P2
//    Return: >0 for P2 left of the line through P0 and P1
//            =0 for P2 on the line
//            <0 for P2 right of the line
let testSide p0 p1 p2 =
   (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);;


let isLeft p0 p1 p2 =
   (testSide p0 p1 p2) > 0.;;


let isRight p0 p1 p2 =
   (testSide p0 p1 p2) < 0.;;
  

let rec _jarvis cmp max_point (hull_list:Point list) (input_list:Point list) =      
   match input_list with
   | p::tl ->
  
       let last = List.hd hull_list in
       if last = max_point then hull_list
       else (
      
         let best = List.fold_left(fun p1 p2 ->
                                      if (cmp last p1 p2) then p2
                                      else p1;  
                                   ) p input_list in
                                  
         _jarvis cmp max_point (best::hull_list) tl
        
       )
      
   | [] -> hull_list;;


let jarvis point_list =
   if List.length point_list < 3 then failwith "3 points are required for a polygon";
  
   let point_list = sort point_list in
   let miny = point_list |> List.hd in       
   let maxy = point_list |> List.rev |> List.hd in
      
   let left = _jarvis (isLeft) maxy [miny] point_list in
   let right = _jarvis (isRight) maxy [miny] point_list in
   right @ left |> List.rev;;