Saturday, July 13, 2013

Detecting faces in your webcam stream with OpenCV/java and displaying back the results

In this tutorial for java we detected the faces on a file picture and display a square around it.

In this series of tutorials we learnt:
  1. Creating windows in java and drawing in them
  2. Capture the camera stream with OpenCV and display using Java libraries (awt/swing).
In this tutorial for C++ we detected the faces and eyes on a input stream from a camera.

Now we want to do the same thing but in Java. Basically translate this C++/OpenCV tutorial into Java/OpenCV.

Got stucked in several places. Even placed a stackoverflow post. Here is the final code (I didn't do the eyes detection but that it is pretty straightforward...). This is actually the 2nd post on the code. This one is an optimized version where I corrected one error (see comments below), changed the way to get the mat (from OpenCV) into BufferedImage (from Java) using the comment below (thank you!), and defined more meaningful objects... The initial older version is further below, for reference... By the way, check also this post benchmarking different methods on how to go from Mat to BufferedImage.
 /*  
  * Captures the camera stream with OpenCV  
  * Search for the faces  
  * Display a circle around the faces using Java
  */  
 import java.awt.*;  
 import java.awt.image.BufferedImage;  
 import java.awt.image.DataBufferByte;  
 import javax.swing.*;  
 import org.opencv.core.Core;  
 import org.opencv.core.Mat;  
 import org.opencv.core.MatOfRect;  
 import org.opencv.core.Point;  
 import org.opencv.core.Rect;  
 import org.opencv.core.Scalar;  
 import org.opencv.core.Size;  
 import org.opencv.highgui.VideoCapture;  
 import org.opencv.imgproc.Imgproc;  
 import org.opencv.objdetect.CascadeClassifier;  
 class My_Panel extends JPanel{  
      private static final long serialVersionUID = 1L;  
      private BufferedImage image;  
      // Create a constructor method  
      public My_Panel(){  
           super();   
      }  
      /**  
       * Converts/writes a Mat into a BufferedImage.  
       *   
       * @param matrix Mat of type CV_8UC3 or CV_8UC1  
       * @return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY  
       */  
      public boolean MatToBufferedImage(Mat matBGR){  
           long startTime = System.nanoTime();  
           int width = matBGR.width(), height = matBGR.height(), channels = matBGR.channels() ;  
           byte[] sourcePixels = new byte[width * height * channels];  
           matBGR.get(0, 0, sourcePixels);  
           // create new image and get reference to backing data  
           image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);  
           final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();  
           System.arraycopy(sourcePixels, 0, targetPixels, 0, sourcePixels.length);  
           long endTime = System.nanoTime();  
           System.out.println(String.format("Elapsed time: %.2f ms", (float)(endTime - startTime)/1000000));  
           return true;  
      }  
      public void paintComponent(Graphics g){  
           super.paintComponent(g);   
           if (this.image==null) return;  
            g.drawImage(this.image,10,10,this.image.getWidth(),this.image.getHeight(), null);  
           //g.drawString("This is my custom Panel!",10,20);  
      }  
 }  
 class processor {  
      private CascadeClassifier face_cascade;  
      // Create a constructor method  
      public processor(){  
           face_cascade=new CascadeClassifier("C:/USERDATA/MEDICAL/CODE/java webcam facedetect/bin/haarcascade_frontalface_alt.xml");  
           if(face_cascade.empty())  
           {  
                System.out.println("--(!)Error loading A\n");  
                 return;  
           }  
           else  
           {  
                      System.out.println("Face classifier loooaaaaaded up");  
           }  
      }  
      public Mat detect(Mat inputframe){  
           Mat mRgba=new Mat();  
           Mat mGrey=new Mat();  
           MatOfRect faces = new MatOfRect();  
           inputframe.copyTo(mRgba);  
           inputframe.copyTo(mGrey);  
           Imgproc.cvtColor( mRgba, mGrey, Imgproc.COLOR_BGR2GRAY);  
           Imgproc.equalizeHist( mGrey, mGrey );  
           face_cascade.detectMultiScale(mGrey, faces);  
           System.out.println(String.format("Detected %s faces", faces.toArray().length));  
           for(Rect rect:faces.toArray())  
           {  
                Point center= new Point(rect.x + rect.width*0.5, rect.y + rect.height*0.5 );  
                Core.ellipse( mRgba, center, new Size( rect.width*0.5, rect.height*0.5), 0, 0, 360, new Scalar( 255, 0, 255 ), 4, 8, 0 );  
           }  
           return mRgba;  
      }  
 }  
 public class window {  
      public static void main(String arg[]){  
       // Load the native library.  
       System.loadLibrary("opencv_java245");       
       String window_name = "Capture - Face detection";  
       JFrame frame = new JFrame(window_name);  
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
    frame.setSize(400,400);  
    processor my_processor=new processor();  
    My_Panel my_panel = new My_Panel();  
    frame.setContentPane(my_panel);       
    frame.setVisible(true);        
       //-- 2. Read the video stream  
        Mat webcam_image=new Mat();  
        VideoCapture capture =new VideoCapture(0);   
    if( capture.isOpened())  
           {  
            while( true )  
            {  
                 capture.read(webcam_image);  
              if( !webcam_image.empty() )  
               {   
                    frame.setSize(webcam_image.width()+40,webcam_image.height()+60);  
                    //-- 3. Apply the classifier to the captured image  
                    webcam_image=my_processor.detect(webcam_image);  
                   //-- 4. Display the image  
                    my_panel.MatToBufferedImage(webcam_image); // We could look at the error...  
                    my_panel.repaint();   
               }  
               else  
               {   
                    System.out.println(" --(!) No captured frame -- Break!");   
                    break;   
               }  
              }  
             }  
             return;  
      }  
 }  

PS.: Check out this video on the Android 3D display I build based on this stuff and the details here.
PS1: Click here to see the index of these series of posts on OpenCV
PS2: This is the older version:
 /*  
  * Captures the camera stream with OpenCV  
  * Search for the faces  
  * Display a circle around the faces  
  */  
 import java.awt.*;  
 import java.awt.image.BufferedImage;  
 import javax.swing.*;  
 import org.opencv.core.Core;  
 import org.opencv.core.Mat;  
 import org.opencv.core.MatOfRect;  
 import org.opencv.core.Point;  
 import org.opencv.core.Rect;  
 import org.opencv.core.Scalar;  
 import org.opencv.core.Size;  
 import org.opencv.highgui.VideoCapture;  
 import org.opencv.imgproc.Imgproc;  
 import org.opencv.objdetect.CascadeClassifier;  
 class My_Panel extends JPanel{  
      private static final long serialVersionUID = 1L;  
      private BufferedImage image;  
      private CascadeClassifier face_cascade;  
      // Create a constructor method  
      public My_Panel(){  
           super();   
           //String face_cascade_name = "/haarcascade_frontalface_alt.xml";  
           //String face_cascade_name = "/lbpcascade_frontalface.xml";  
           //-- 1. Load the cascades  
           //String str;  
           //str = getClass().getResource(face_cascade_name).getPath();  
           //str = str.replace("/C:","C:");  
           //face_cascade_name=str;  
           //face_cascade=new CascadeClassifier(face_cascade_name);  
           face_cascade=new CascadeClassifier("C:/USERDATA/MEDICAL/CODE/java webcam facedetect/bin/haarcascade_frontalface_alt.xml");  
           //face_cascade=new CascadeClassifier("C:/eclipse/opencv/data/haarcascades/haarcascade_frontalface_alt.xml");  
           if(face_cascade.empty())  
           {  
                System.out.println("--(!)Error loading A\n");  
                 return;  
           }  
           else  
           {  
                      System.out.println("Face classifier loooaaaaaded up");  
           }  
      }  
      private BufferedImage getimage(){  
           return image;  
      }  
      public void setimage(BufferedImage newimage){  
           image=newimage;  
           return;  
      }  
      /**  
       * Converts/writes a Mat into a BufferedImage.  
       *   
       * @param matrix Mat of type CV_8UC3 or CV_8UC1  
       * @return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY  
       */  
      public BufferedImage matToBufferedImage(Mat matrix) {  
        int cols = matrix.cols();  
        int rows = matrix.rows();  
        int elemSize = (int)matrix.elemSize();  
        byte[] data = new byte[cols * rows * elemSize];  
        int type;  
        matrix.get(0, 0, data);  
        switch (matrix.channels()) {  
          case 1:  
            type = BufferedImage.TYPE_BYTE_GRAY;  
            break;  
          case 3:   
            type = BufferedImage.TYPE_3BYTE_BGR;  
            // bgr to rgb  
            byte b;  
            for(int i=0; i<data.length; i=i+3) {  
              b = data[i];  
              data[i] = data[i+2];  
              data[i+2] = b;  
            }  
            break;  
          default:  
            return null;  
        }  
        BufferedImage image2 = new BufferedImage(cols, rows, type);  
        image2.getRaster().setDataElements(0, 0, cols, rows, data);  
        return image2;  
      }  
      public void paintComponent(Graphics g){  
            BufferedImage temp=getimage();  
            g.drawImage(temp,10,10,temp.getWidth(),temp.getHeight(), this);   
      }  
      public Mat detect(Mat inputframe){  
           Mat mRgba=new Mat();  
           Mat mGrey=new Mat();  
           MatOfRect faces = new MatOfRect();  
           inputframe.copyTo(mRgba);  
           inputframe.copyTo(mGrey);  
           Imgproc.cvtColor( mRgba, mGrey, Imgproc.COLOR_BGR2GRAY);  
           Imgproc.equalizeHist( mGrey, mGrey );  
           face_cascade.detectMultiScale(mGrey, faces);  
           //face_cascade.detectMultiScale(mGrey, faces, 1.1, 2, 0|Objdetect.CASCADE_SCALE_IMAGE, new Size(30, 30), new Size(200,200) );  
           //face_cascade.detectMultiScale(mGrey, faces, 1.1, 2, 2//CV_HAAR_SCALE_IMAGE,  
           //          ,new Size(30, 30), new Size(200,200) );  
           System.out.println(String.format("Detected %s faces", faces.toArray().length));  
           for(Rect rect:faces.toArray())  
           {  
                Point center= new Point(rect.x + rect.width*0.5, rect.y + rect.height*0.5 );  
                Core.ellipse( mRgba, center, new Size( rect.width*0.5, rect.height*0.5), 0, 0, 360, new Scalar( 255, 0, 255 ), 4, 8, 0 );  
           }  
           return mRgba;  
           }  
 }  
 public class window {  
      public static void main(String arg[]){  
       // Load the native library.  
       System.loadLibrary("opencv_java245");       
       String window_name = "Capture - Face detection";  
       JFrame frame = new JFrame(window_name);  
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
    frame.setSize(400,400);  
    My_Panel my_panel = new My_Panel();  
    frame.setContentPane(my_panel);       
    frame.setVisible(true);        
       //-- 2. Read the video stream  
    BufferedImage temp;  
        Mat webcam_image=new Mat();  
        VideoCapture capture =new VideoCapture(0);   
    if( capture.isOpened())  
           {  
            while( true )  
            {  
                 capture.read(webcam_image);  
              if( !webcam_image.empty() )  
               {   
                    frame.setSize(webcam_image.width()+40,webcam_image.height()+60);  
                    //-- 3. Apply the classifier to the captured image  
                    // At this point I was wondering where this should be done.  
                    // I put it within the panel class, but maybe one could actually  
                    // create a processor object...  
                    webcam_image=my_panel.detect(webcam_image);  
                   //-- 4. Display the image  
                    temp=my_panel.matToBufferedImage(webcam_image);  
                    my_panel.setimage(temp);  
                    my_panel.repaint();   
               }  
               else  
               {   
                    System.out.println(" --(!) No captured frame -- Break!");   
                    break;   
               }  
              }  
             }  
             return;  
      }  
 }  

Tuesday, July 9, 2013

How to get music out of Youtube

Sorry, I use Firefox, so, not sure what other alternatives are out there, but here is what I do:
  1. Install Firefox
  2. Download and install the DownloadHelper extension: http://www.downloadhelper.net/
  3. Download helper has the free version that allows you to download the original video, on different resolutions, but usually limited to few video formats (.flv). So, the question is how do you get the mp3. Two options... One is to get the paid version of DownloadHelper, who can do the conversion... (please see PS2 below)
  4. The second option is to download AoA Audio Extractor.
  5. Finally, on my case, I burn a CD for the car... I use CDBurnerXP.
Of course, this allows you to download all kind of music, people's mixes, their own creations, etc... that they share for free, but also copyrighted material. Please consider paying for it if you can afford it. Songs today are $1...

Cheers!

PS.: One more thing... Sometimes you want to pass your CD to mp3 Converter... I use Free CD to mp3 from eusing.com
PS2.: The guys at downloadhelper support the Kiva initiative, which lends money to 3rd world businesses to help on the development. Please see here and consider donating.
PS3.: Another free nice software that helps you convert between audio formats is from Koyotesoft

Monday, July 8, 2013

Grouping your posts by categories

Here they describe the major thoughts on how to arrange this...
Basically:
  1. Add labels to your posts (you do that on the screen where you edit your posts...).
  2. Go to Design-->Layout and click "Add a Gadget" wherever you want to add it. For instance, in the image below, before we add what shows on the image as "Categories", we had something on its place like the "Add a Gadget" on the right.
  3. Select Link List. 
  4. Filling in the information is quite straightforward. See this example:
    The only question could be where does one get the address... Basically, when you click on the label, all the posts with that label show up on the page. Copy it from the browser address bar.
Rest should be straightforward...

Sunday, July 7, 2013

Quick intro to OpenCV

Just couple of links that is worth reading on OpenCV to get quickly up to speed or to find a example... Listed from top to bottom:
  1. A quick lecture/pdf on OpenCV.
  2. OpenCV examples
  3. Cheat sheet to OpenCV
PS.: Click here to see the index of these series of posts on OpenCV

Tracking a ball with Java/OpenCV

Here we are going to explain how to track a ball. There are few places in the web that explain similar things (see PS) but without much details. So, I am going to put few posts explaining what I learned. Hopefully it is useful to you... Just to call your attention on few topics:
  1. Basically we are capturing images like we have done so far.
  2. Then we detect the ball in two ways and do an AND of both. One way is by color segmentation, and the other by the shape.
  3. You can learn here about how to select objects by color. Basically, you got to segment the image in the HSV space, which is much easier than on RGB.
  4. The instruction for thresholding is "Core.inRange(distance,new Scalar(0.0), new Scalar(200.0), thresholded2);"
  5. Notice that we do two thresholdings, one over the Hue, and another over the saturation/value of the sample. We combine both here "Core.bitwise_and(thresholded, thresholded2, thresholded);"
  6. After that we blur out noise (smoothing): "Imgproc.GaussianBlur(thresholded, thresholded, new Size(9,9),0,0);" By the way, somewhere I read that this may be included by default in the Canny transform. Not sure if right or not, but I left it, just in case.
  7. Then we apply the Hough Circles Transform. I detailed further this in this post. And you are done!
Other things that are interesting, and just list here, for search engines to find out:
  1. How to initialize an array (Mat) to a constant value: "Mat array255=new Mat(webcam_image.height(),webcam_image.width(),CvType.CV_8UC1);
            array255.setTo(new Scalar(255)); "
  2. Notice in the code how the individual channels in the HSV Mat are extracted (split the channels)."List<Mat> lhsv = new ArrayList<Mat>(3); Core.split(hsv_image, lhsv); /*We get 3 2D one channel Mat*/ Mat S = lhsv.get(1); Mat V = lhsv.get(2);"
  3. How to convert from one type of Mat to another: S.convertTo(S, CvType.CV_32F);
  4. Read the code to understand how to position text in the screen. For instance, don't use JLabel to position text in the screen. It can't. It can only do center, or left, or stuff like that... JTextField may do it, but we can also use OpenCV, which is what I did...  
  5. Notice that to display the final image, first you do everything in the Mat and then upload it to panel/image.
See below for the final code. I apologize for not being all that clean, but I got to keep going with the project. Here are some screenshots of the results :)

The original BGR with the resulting detection (purple circle around the ball):
The HSV:
 The distance to the maximum saturation/value: sqrt((255-S)^2+(255-V)^2)):
 The results after a Canny transform (edge detection): Imgproc.Canny(thresholded, thresholded, 500, 250);


The Canny transform happens inside the Hough Circles Transform, but here I had it apart just for display. Unfortunately I didn't get a picture of the result after HSV threshold and SV distance are AND together.

Overall, to tell you the truth, worked pretty well, but sometimes I got false detections on my face and also no detections if I move the ball fast. I believe the secret to it is in the adjustment of the parameters, but in the end, there is only so much one can do (imagine the background had circular figures of the same color as the ball...). We have other methods in mind that will help the performance of our overall project, mainly consisting on time filtering (a ball can't just appear and disappear instantly...)

Here is the code. I used to get an exception but no more, thanks to Abdel's comment below! Thank you! :)
 import java.awt.Graphics;  
 import java.awt.image.BufferedImage;  
 import java.util.ArrayList;  
 import java.util.List;  
 import javax.swing.JFrame;  
 import javax.swing.JPanel;  
 import org.opencv.core.Core;  
 import org.opencv.core.Mat;   
 import org.opencv.core.Point;  
 import org.opencv.core.Scalar;  
 import org.opencv.core.Size;  
 import org.opencv.highgui.VideoCapture;  
 import org.opencv.imgproc.Imgproc;  
 import org.opencv.core.CvType;  
 class Panel extends JPanel{  
   private static final long serialVersionUID = 1L;  
   private BufferedImage image;    
   // Create a constructor method  
   public Panel(){  
     super();  
   }  
   private BufferedImage getimage(){  
     return image;  
   }  
   public void setimage(BufferedImage newimage){  
     image=newimage;  
     return;  
   }  
   public void setimagewithMat(Mat newimage){  
     image=this.matToBufferedImage(newimage);  
     return;  
   }  
   /**  
    * Converts/writes a Mat into a BufferedImage.  
    *  
    * @param matrix Mat of type CV_8UC3 or CV_8UC1  
    * @return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY  
    */  
   public BufferedImage matToBufferedImage(Mat matrix) {  
     int cols = matrix.cols();  
     int rows = matrix.rows();  
     int elemSize = (int)matrix.elemSize();  
     byte[] data = new byte[cols * rows * elemSize];  
     int type;  
     matrix.get(0, 0, data);  
     switch (matrix.channels()) {  
       case 1:  
         type = BufferedImage.TYPE_BYTE_GRAY;  
         break;  
       case 3:  
         type = BufferedImage.TYPE_3BYTE_BGR;  
         // bgr to rgb  
         byte b;  
         for(int i=0; i<data.length; i=i+3) {  
           b = data[i];  
           data[i] = data[i+2];  
           data[i+2] = b;  
         }  
         break;  
       default:  
         return null;  
     }  
     BufferedImage image2 = new BufferedImage(cols, rows, type);  
     image2.getRaster().setDataElements(0, 0, cols, rows, data);  
     return image2;  
   }  
   @Override  
   protected void paintComponent(Graphics g){  
      super.paintComponent(g);  
      //BufferedImage temp=new BufferedImage(640, 480, BufferedImage.TYPE_3BYTE_BGR);  
      BufferedImage temp=getimage();  
      //Graphics2D g2 = (Graphics2D)g;
      if( temp != null)
        g.drawImage(temp,10,10,temp.getWidth(),temp.getHeight(), this);  
   }  
 }  
 public class ball {  
   public static void main(String arg[]){  
     // Load the native library.  
     System.loadLibrary("opencv_java245");  
     // It is better to group all frames together so cut and paste to  
     // create more frames is easier  
     JFrame frame1 = new JFrame("Camera");  
     frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     frame1.setSize(640,480);  
     frame1.setBounds(0, 0, frame1.getWidth(), frame1.getHeight());  
     Panel panel1 = new Panel();  
     frame1.setContentPane(panel1);  
     frame1.setVisible(true);  
     JFrame frame2 = new JFrame("HSV");  
     frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     frame2.setSize(640,480);  
     frame2.setBounds(300,100, frame2.getWidth()+300, 100+frame2.getHeight());  
     Panel panel2 = new Panel();  
     frame2.setContentPane(panel2);  
     frame2.setVisible(true);  
     JFrame frame3 = new JFrame("S,V Distance");  
     frame3.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     frame3.setSize(640,480);  
     frame3.setBounds(600,200, frame3.getWidth()+600, 200+frame3.getHeight());  
     Panel panel3 = new Panel();  
     frame3.setContentPane(panel3);  
     frame3.setVisible(true);  
     JFrame frame4 = new JFrame("Threshold");  
     frame4.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     frame4.setSize(640,480);  
     frame4.setBounds(900,300, frame3.getWidth()+900, 300+frame3.getHeight());  
     Panel panel4 = new Panel();  
     frame4.setContentPane(panel4);      
     frame4.setVisible(true);  
     //-- 2. Read the video stream  
     VideoCapture capture =new VideoCapture(0);  
     Mat webcam_image=new Mat();  
     Mat hsv_image=new Mat();  
     Mat thresholded=new Mat();  
     Mat thresholded2=new Mat();  
      capture.read(webcam_image);  
      frame1.setSize(webcam_image.width()+40,webcam_image.height()+60);  
      frame2.setSize(webcam_image.width()+40,webcam_image.height()+60);  
      frame3.setSize(webcam_image.width()+40,webcam_image.height()+60);  
      frame4.setSize(webcam_image.width()+40,webcam_image.height()+60);  
     Mat array255=new Mat(webcam_image.height(),webcam_image.width(),CvType.CV_8UC1);  
     array255.setTo(new Scalar(255));  
     /*Mat S=new Mat();  
     S.ones(new Size(hsv_image.width(),hsv_image.height()),CvType.CV_8UC1);  
     Mat V=new Mat();  
     V.ones(new Size(hsv_image.width(),hsv_image.height()),CvType.CV_8UC1);  
         Mat H=new Mat();  
     H.ones(new Size(hsv_image.width(),hsv_image.height()),CvType.CV_8UC1);*/  
     Mat distance=new Mat(webcam_image.height(),webcam_image.width(),CvType.CV_8UC1);  
     //new Mat();//new Size(webcam_image.width(),webcam_image.height()),CvType.CV_8UC1);  
     List<Mat> lhsv = new ArrayList<Mat>(3);      
     Mat circles = new Mat(); // No need (and don't know how) to initialize it.  
                  // The function later will do it... (to a 1*N*CV_32FC3)  
     Scalar hsv_min = new Scalar(0, 50, 50, 0);  
     Scalar hsv_max = new Scalar(6, 255, 255, 0);  
     Scalar hsv_min2 = new Scalar(175, 50, 50, 0);  
     Scalar hsv_max2 = new Scalar(179, 255, 255, 0);  
     double[] data=new double[3];  
     if( capture.isOpened())  
     {  
      while( true )  
      {  
        capture.read(webcam_image);  
        if( !webcam_image.empty() )  
         {  
          // One way to select a range of colors by Hue  
          Imgproc.cvtColor(webcam_image, hsv_image, Imgproc.COLOR_BGR2HSV);  
          Core.inRange(hsv_image, hsv_min, hsv_max, thresholded);           
          Core.inRange(hsv_image, hsv_min2, hsv_max2, thresholded2);  
          Core.bitwise_or(thresholded, thresholded2, thresholded);  
          // Notice that the thresholds don't really work as a "distance"  
          // Ideally we would like to cut the image by hue and then pick just  
          // the area where S combined V are largest.  
          // Strictly speaking, this would be something like sqrt((255-S)^2+(255-V)^2)>Range  
          // But if we want to be "faster" we can do just (255-S)+(255-V)>Range  
          // Or otherwise 510-S-V>Range  
          // Anyhow, we do the following... Will see how fast it goes...  
          Core.split(hsv_image, lhsv); // We get 3 2D one channel Mats  
          Mat S = lhsv.get(1);  
          Mat V = lhsv.get(2);  
          Core.subtract(array255, S, S);  
          Core.subtract(array255, V, V);  
          S.convertTo(S, CvType.CV_32F);  
          V.convertTo(V, CvType.CV_32F);  
          Core.magnitude(S, V, distance);  
          Core.inRange(distance,new Scalar(0.0), new Scalar(200.0), thresholded2);  
          Core.bitwise_and(thresholded, thresholded2, thresholded);  
          // Apply the Hough Transform to find the circles  
          Imgproc.GaussianBlur(thresholded, thresholded, new Size(9,9),0,0);  
          Imgproc.HoughCircles(thresholded, circles, Imgproc.CV_HOUGH_GRADIENT, 2, thresholded.height()/4, 500, 50, 0, 0);   
          //Imgproc.Canny(thresholded, thresholded, 500, 250);  
          //-- 4. Add some info to the image  
          Core.line(webcam_image, new Point(150,50), new Point(202,200), new Scalar(100,10,10)/*CV_BGR(100,10,10)*/, 3);  
          Core.circle(webcam_image, new Point(210,210), 10, new Scalar(100,10,10),3);  
          data=webcam_image.get(210, 210);  
          Core.putText(webcam_image,String.format("("+String.valueOf(data[0])+","+String.valueOf(data[1])+","+String.valueOf(data[2])+")"),new Point(30, 30) , 3 //FONT_HERSHEY_SCRIPT_SIMPLEX  
               ,1.0,new Scalar(100,10,10,255),3);  
          //int cols = circles.cols();  
           int rows = circles.rows();  
           int elemSize = (int)circles.elemSize(); // Returns 12 (3 * 4bytes in a float)  
           float[] data2 = new float[rows * elemSize/4];  
           if (data2.length>0){  
             circles.get(0, 0, data2); // Points to the first element and reads the whole thing  
                           // into data2  
             for(int i=0; i<data2.length; i=i+3) {  
               Point center= new Point(data2[i], data2[i+1]);  
               //Core.ellipse( this, center, new Size( rect.width*0.5, rect.height*0.5), 0, 0, 360, new Scalar( 255, 0, 255 ), 4, 8, 0 );  
               Core.ellipse( webcam_image, center, new Size((double)data2[i+2], (double)data2[i+2]), 0, 0, 360, new Scalar( 255, 0, 255 ), 4, 8, 0 );  
             }  
           }  
          Core.line(hsv_image, new Point(150,50), new Point(202,200), new Scalar(100,10,10)/*CV_BGR(100,10,10)*/, 3);  
          Core.circle(hsv_image, new Point(210,210), 10, new Scalar(100,10,10),3);  
          data=hsv_image.get(210, 210);  
          Core.putText(hsv_image,String.format("("+String.valueOf(data[0])+","+String.valueOf(data[1])+","+String.valueOf(data[2])+")"),new Point(30, 30) , 3 //FONT_HERSHEY_SCRIPT_SIMPLEX  
               ,1.0,new Scalar(100,10,10,255),3);  
          distance.convertTo(distance, CvType.CV_8UC1);  
          Core.line(distance, new Point(150,50), new Point(202,200), new Scalar(100)/*CV_BGR(100,10,10)*/, 3);  
          Core.circle(distance, new Point(210,210), 10, new Scalar(100),3);  
          data=(double[])distance.get(210, 210);  
          Core.putText(distance,String.format("("+String.valueOf(data[0])+")"),new Point(30, 30) , 3 //FONT_HERSHEY_SCRIPT_SIMPLEX  
               ,1.0,new Scalar(100),3);   
          //-- 5. Display the image  
          panel1.setimagewithMat(webcam_image);  
            panel2.setimagewithMat(hsv_image);  
          //panel2.setimagewithMat(S);  
            //distance.convertTo(distance, CvType.CV_8UC1);  
            panel3.setimagewithMat(distance);  
           panel4.setimagewithMat(thresholded);  
            frame1.repaint();  
            frame2.repaint();  
            frame3.repaint();  
          frame4.repaint();  
         }  
         else  
         {  
           System.out.println(" --(!) No captured frame -- Break!");  
           break;  
         }  
        }  
       }  
     return;  
   }  
 }   

PS.: Other examples on the same exercise:
  1. A lot of the learning came from this one. Unfortunately no java or deep level of explanation... so, took me a while to make it work... 
  2. Different one which I didn't use, but may be useful...
  3. And one more.
  4. In this example the software will give you the angle set by the line between two cursors of different colors and the horizontal.
PS2.: Click here to see the index of these series of posts on OpenCV

RGB and HSV. Applying to color based detection.

HSV stands for Hue, Saturation and Value/Brightness. Basically Hue defines what we understand as "color". The easiest way to understand this is to download the software on this link and play with it... Just by moving the Hue slider, you'll move the pointer in the horizontal color bar below. Nothing else will change. And then, the big square with gradients/shades will change its color. For instance, when Hue is 61, you are over the green (see the two white marks), and the big square is green.


When you move the slider to Hue 129, then you are in the blue:


Saturation tells us how much of that color we have. The way I think about it (not sure if right or not) is to imagine a paint manufactured by adding a powder tint to water. The bigger the number, the more tint you add to the water. That moves the cross in the square from left to right. Notice how the rightest values are "very" blue.


Finally, Brightness tells us how "dark" that color is... Again, the way I look at it is how much light you would put to see the color of the paint. If you almost don't illuminate the water+tint mix, you will see it very dark, while if you put a lot of light, then it will eventually very bright. Moving the slider will move the cross in the square up and down. The others stay constant.

And why to we care about this? Because basically we can easily segment the image by picking values of HSV that are close to each other. See the code here to understand that. In that example, we want to pick a red ball, so, we first segment the Hue (first value on the triplet) by looking at Hue<6 or Hue>175.

Then, we want to pick like a sphere of value around those colors. The sphere that contains more saturated reds or lightest ones. We don't want something that looks like grey or black... We do by computing the distance of (S,V) to (255,255).

Finally we AND both to pick the pixels that match both conditions.

Hope this clarifies whole thing. Just some final remarks.
  1. How to find what is our HSV? The code that I showed here actually does it real time on the screen. Very useful to debug. Other ways to do it is using the above utility, or you can just use MS Paint or GIMP. Other util which I didn't actually even tried...
  2. Notice that value range of HSV change from program to program/explanation. In Wikipedia, the ranges seem to be H: 0-360, S:0-1, V:0-1. But in OpenCV, these are H: 0 - 179, S: 0 - 255, V: 0 - 255. See here for more info. FYI MS Paint uses 0-240 for all 3 (HSV).
Other links of interest:
  1. Color in OpenCV explained here
  2. Good tutorial/app (the shown above).
  3. A tutorial here.
PS.: Click here to see the index of these series of posts on OpenCV

A way to insert code in posts for Blogger

For a way to insert the code in blogger, I used the instructions here:
http://codeformatter.blogspot.com/

Also, I found this, but I didn't use it:
http://www.craftyfella.com/2010/01/syntax-highlighting-with-blogger-engine.html