Saturday, September 17, 2011

Blobs with opencv (internal function)

There are many open source opencv BLOB libraries that you can use. i have tried several of these, however because of the 64 bit machine that im using recompiling these are very troublesome.

If you have heard of these libraries:
  1. "cvBlobsLib": http://opencv.willowgarage.com/wiki/cvBlobsLib 
  2. "cvBlob": http://code.google.com/p/cvblob/ 
  3. "Bloblib" by Dave Grossman (also referred to as "Blob Analysis Package"):
    Go to http://tech.groups.yahoo.com/group/OpenCV/files/
 You will know that opencv also has a built in function that can help you find blobs using cv::findContour and see some statistics using the cv::moments.
eventually make these functions similar to regionprops Matlab function


The BLOB FINDER CLASS:
class atsBlobFinder
{
public:
    atsBlobFinder(cv::Mat src)
    {
        numBlobs = 0;
        cv::Mat img; //must create a temporary Matrix to hold the gray scale or wont work
        cv::cvtColor(src,img,CV_BGR2GRAY); //Convert image to GrayScale
        img = img > 1; //create the binary image
        ////cv::adaptiveThreshold(src,src,64,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,7,13); //create a binary image
       
        findContours( img, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find the Contour BLOBS
        vector<Moments> _mu(contours.size() );
        vector<Point2f> _mc( contours.size() );
        for( int i = 0; i < contours.size(); i++ )
        {
            _mu[i] = moments( Mat(contours[i]), false );
            _mc[i] = Point2f( _mu[i].m10/_mu[i].m00 , _mu[i].m01/_mu[i].m00);
        }
        mu = _mu;
        mc = _mc;
        numBlobs = contours.size();
    }
    void Draw(cv::Mat &dst)
    {
        // iterate through all the top-level contours,
        // draw each connected component with its own random color
        for( int i = 0; i < contours.size(); i++ )
        {
            Scalar color(  rng.uniform(0,255),  rng.uniform(0,255),  rng.uniform(0,255) );
            drawContours( dst, contours, i, color, CV_FILLED, 8, hierarchy );
            // drawCross(mc[i],Scalar(0,0,255), 5,dst); //put a cross
            char buff[255];
            sprintf(buff, "%d", i);

            string text = std::string(buff);
            cv::putText(dst,text,mc[i],0,0.5,Scalar(0,0,255),1,8,false);
        }
    }
    int getNumBlobs()
    {
        //need to create a buffer for output or wrong reference
        /*char buff[255];
        sprintf(buff, "%d", numBlobs);*/
        return numBlobs;
    }
private:
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    vector<Moments> mu;
    vector<Point2f> mc;
    int numBlobs;
};




#include <iostream>

// Include OpenCV
#include <opencv/cv.h>
#include <opencv/highgui.h>

using namespace cv;
#define drawCross( center, color, d, drawing )                                 \
            line( drawing, Point( center.x - d, center.y - d ),                \
            Point( center.x + d, center.y + d ), color, 2, CV_AA, 0); \
            line( drawing, Point( center.x + d, center.y - d ),                \
            Point( center.x - d, center.y + d ), color, 2, CV_AA, 0 )

RNG rng(12345);
int main( int argc, char** argv )
{
    Mat src;
    // the first command line parameter must be file name of binary
    // (black-n-white) image
    if(!(src=imread("pic6.png", CV_LOAD_IMAGE_GRAYSCALE)).data)
    {
        printf("OOOPS");
        waitKey(0);
        return -1;
    }



    Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);

    src = src > 1;
    //cv::adaptiveThreshold(src,src,64,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,7,13);
    namedWindow( "Source", 1 );
    imshow( "Source", src );

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours( src, contours, hierarchy,
        CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

    /// Get the moments
    vector<Moments> mu(contours.size() );
    vector<Point2f> mc( contours.size() );
    for( int i = 0; i < contours.size(); i++ )
        {
            mu[i] = moments( Mat(contours[i]), false );
            mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00);
        }

    // iterate through all the top-level contours,
    // draw each connected component with its own random color
    for( int i = 0; i < contours.size(); i++ )
    {
        Scalar color(  rng.uniform(0,255),  rng.uniform(0,255),  rng.uniform(0,255) );
        drawContours( dst, contours, i, color, CV_FILLED, 8, hierarchy );
        // drawCross(mc[i],Scalar(0,0,255), 5,dst); //put a cross
         char buff[255];
        sprintf(buff, "%d", i);

         string text = std::string(buff);
        cv::putText(dst,text,mc[i],0,0.5,Scalar(0,0,255),1,8,false);
    }
   

    namedWindow( "Components", 1 );
    imshow( "Components", dst );
    waitKey(0);
}

finding the center of gravity in opencv





Alot of feature detectors such as the Haar classifier will return rectangular shapes presented as a detected feature.

one of the things to do in order to track these, is to find the center of the rectangle.


int main( int argc, char** argv )
{
atscameraCapture movie;
char code = (char)-1;
for(;;)
        {
            //get camera
            cv::Mat imgs = movie.ats_getImage();

#define drawCross( center, color, d, img )                                 \
            line( img, Point( center.x - d, center.y - d ),                \
            Point( center.x + d, center.y + d ), color, 2, CV_AA, 0); \
            line( img, Point( center.x + d, center.y - d ),                \
            Point( center.x - d, center.y + d ), color, 2, CV_AA, 0 )

            cv::Point center;

            //given 2 points representing the rectangle
            cv::Point topLeft(100,100);
            cv::Point bottomRight(200,200);
            cv::rectangle(imgs,topLeft,bottomRight,Scalar( 0, 255, 255 ),-1, 8 );

            //compute for center of triangle
            center.x = (topLeft.x+bottomRight.x)/2;
            center.y = (topLeft.y+bottomRight.y)/2;
            drawCross(center,Scalar(0,0,255), 5,imgs);

            imshow("camera",  imgs);
            code = (char)waitKey(100);
           
             if( code == 27 || code == 'q' || code == 'Q' )
            break;
        }
 return 0;

}

Another way of finding the center of gravity is using the internal function of OPENCV called cv::moments

Mat canny_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;
  RNG rng(12345);

/** @function main */
int main( int argc, char** argv )
{
atscameraCapture movie;
char code = (char)-1;
for(;;)
        {
            //get camera
            cv::Mat src_gray;
            cv::Mat imgs = movie.ats_getImage();

#define drawCross( center, color, d, img )                                 \
            line( img, Point( center.x - d, center.y - d ),                \
            Point( center.x + d, center.y + d ), color, 2, CV_AA, 0); \
            line( img, Point( center.x + d, center.y - d ),                \
            Point( center.x - d, center.y + d ), color, 2, CV_AA, 0 )

             /// Convert image to gray and blur it
              cvtColor( imgs, src_gray, CV_BGR2GRAY );
              blur( src_gray, src_gray, Size(3,3) );


           

              /// Detect edges using canny
              Canny( src_gray, canny_output, 10, 50, 3 );
              /// Find contours
              findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

              /// Get the moments
              vector<Moments> mu(contours.size() );
              for( int i = 0; i < contours.size(); i++ )
                 { mu[i] = moments( Mat(contours[i]), false ); }

              ///  Get the mass centers:
              vector<Point2f> mc( contours.size() );
              for( int i = 0; i < contours.size(); i++ )
                 { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );
              }
               Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
              for( int i = 0; i< contours.size(); i++ )
                 {
                   Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
                   drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
                   circle( drawing, mc[i], 4, color, -1, 8, 0 );
            printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", i, mu[i].m00, contourArea(Mat(contours[i])), arcLength( Mat(contours[i]), true ) );
      


              }

              namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
              imshow( "Contours", drawing );

            //imshow("camera",  src_gray);
            code = (char)waitKey(1000);
           
             if( code == 27 || code == 'q' || code == 'Q' )
            break;
        }
 return 0;

}

Monday, September 12, 2011

2D/3D estimation using solvePnP in opencv (NOT SOLVED)

In opencv "solvePnP" is used to find known points on a known 3D object. doing so the objects orientation relative to the camera coordinate system can be found.
the function is equivalent to finding the extrinsic camera parameters. which makes me believe its more for planar objects. need to do a few more experiments to find out why.

im using code from:
http://www.morethantechnical.com/2010/03/19/quick-and-easy-head-pose-estimation-with-opencv-w-code/  




#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>


#include <iostream>
#include <string>


using namespace cv;

#include <vector>

using namespace std;

#include <GL/gl.h>
#include <GL/glu.h>
#include <glut.h>

void loadNext();
void loadWithPoints(Mat& ip, Mat& img);

const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };

double rot[9] = {0};
Vec3d eav;
GLuint textureID;
Mat backPxls;
vector<double> rv(3), tv(3);
Mat rvec(rv),tvec(tv);
Mat camMatrix;

void resize(int width, int height)
{
    const float ar = (float) width / (float) height;

    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(40,1.0,0.01,1000.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity() ;
}

int __w=250,__h=250;

void key(unsigned char key, int x, int y)
{
    //static int counter = 0;

    switch (key)
    {
    case 27 :
    case 'Q':
    case 'q':
        break;
    case ' ':
        loadNext();

        glBindTexture(GL_TEXTURE_2D, textureID);//This binds the texture to a texture target
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//set our filter
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);    //set our filter
        glTexImage2D(GL_TEXTURE_2D, 0, 3, backPxls.cols, backPxls.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, backPxls.data);

        break;
    default:
        break;
    }

    glutPostRedisplay();
}

void idle(void)
{
    glutPostRedisplay();
}



void myGLinit() {
    glClearColor(1,1,1,1);

    glShadeModel(GL_SMOOTH);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glEnable(GL_LIGHT0);
    glEnable(GL_NORMALIZE);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );

    glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

    glEnable(GL_LIGHTING);

    glGenTextures(1, &textureID);
}



void display(void)
{
    glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear Screen And Depth Buffer

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(0,0,0,0,0,1,0,1,0);

    //----------Axes
    glPushMatrix();
    glTranslated(0,0,5);

    glPushMatrix();
    double _d[16] = {    rot[0],rot[1],rot[2],0,
                        rot[3],rot[4],rot[5],0,
                        rot[6],rot[7],rot[8],0,
                        0,       0,      0        ,1};
    glMultMatrixd(_d);
    glRotated(180,1,0,0);

    //Z = red
    glPushMatrix();
    glRotated(180,0,1,0);
    glColor3d(1,0,0);
    glutSolidCone(0.05,1,15,20);
    glTranslated(0,0,1);
    glScaled(.1,.1,.1);
    glutSolidTetrahedron();
    glPopMatrix();

    //Y = green
    glPushMatrix();
    glRotated(-90,1,0,0);
    glColor3d(0,1,0);
    glutSolidCone(0.05,1,15,20);
    glTranslated(0,0,1);
    glScaled(.1,.1,.1);
    glutSolidTetrahedron();
    glPopMatrix();

    //X = blue
    glPushMatrix();
    glRotated(-90,0,1,0);
    glColor3d(0,0,1);
    glutSolidCone(0.05,1,15,20);
   
    glTranslated(0,0,1);
    glScaled(.1,.1,.1);
    glutSolidTetrahedron();
    glPopMatrix();

    glPopMatrix();
    glPopMatrix();
    //----------End axes

    glutSwapBuffers();
}

int start_opengl_with_stereo(int argc,char** argv) {
    glutInitWindowSize(250,250);
    glutInitWindowPosition(40,40);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
    glutCreateWindow("3D points coordinate");

    myGLinit();

    glutReshapeFunc(resize);
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);

    glutMainLoop();

    return 1;
}

Mat op;

void loadNext() {
    vector<Point2f > points;

float x,y;
 
  x=282;y=274;
  points.push_back(cv::Point2f(x,y));
  x=397;y=227;
  points.push_back(cv::Point2f(x,y));
  x=577;y=271;
  points.push_back(cv::Point2f(x,y));
  x=462;y=318;
  points.push_back(cv::Point2f(x,y));
  x=270;y=479;
  points.push_back(cv::Point2f(x,y));
  x=450;y=523;
  points.push_back(cv::Point2f(x,y));
  x=566;y=475;
  points.push_back(cv::Point2f(x,y));

    Mat ip(points);

    Mat img =  Mat::zeros( 800, 600, CV_8UC3 );
    for(unsigned int i = 0; i < points.size(); ++i)
    {
    std::cout << points[i] << std::endl;
    cv::circle(img,points[i],2,cv::Scalar(0, 0, 255, 0),1,8,0);
    }
    loadWithPoints(ip,img);

}

void loadWithPoints(Mat& ip, Mat& img) {
    double _dc[] = {0,0,0,0};
    solvePnP(op,ip,camMatrix,Mat(1,4,CV_64FC1,_dc),rvec,tvec,true);

    Mat rotM(3,3,CV_64FC1,rot);
    Rodrigues(rvec,rotM);
    double* _r = rotM.ptr<double>();

    Mat tmp,tmp1,tmp2,tmp3,tmp4,tmp5;
    double _pm[12] = {_r[0],_r[1],_r[2],0,
                      _r[3],_r[4],_r[5],0,
                      _r[6],_r[7],_r[8],0};
    decomposeProjectionMatrix(Mat(3,4,CV_64FC1,_pm),tmp,tmp1,tmp2,tmp3,tmp4,tmp5,eav);   
    imshow("tmp",img);
}


int main(int argc, char** argv)
{

    vector<Point3f > modelPoints;
    float x,y,z;
 
  x=.5;y=.5;z=-.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=.5;y=.5;z=.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=-.5;y=.5;z=.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=-.5;y=.5;z=-.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=.5;y=-.5;z=-.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=-.5;y=-.5;z=-.5;
  modelPoints.push_back(cv::Point3f(x,y,z));
  x=-.5;y=-.5;z=.5;
  modelPoints.push_back(cv::Point3f(x,y,z));


    op = Mat(modelPoints);

    rvec = Mat(rv);
    double _d[9] = {1,0,0,
                    0,-1,0,
                    0,0,-1};
    Rodrigues(Mat(3,3,CV_64FC1,_d),rvec);
    tv[0]=0;tv[1]=0;tv[2]=1;
    tvec = Mat(tv);
    double _cm[9] = { 40, 0, 400,
                      0, 40, 500,
                      0,  0,   40 }; //caliberation matrix PROBLEM!?
    camMatrix = Mat(3,3,CV_64FC1,_cm);

    namedWindow("tmp",1);
    loadNext();

    start_opengl_with_stereo(argc,argv);

    return 0;
}

Opencv with OpenGL (installation and trials)

so i needed to install opengl for a 2D/3D test using opencv

since Opengl libraries come preinstalled just needed to download GLUT from:
http://www.xmission.com/~nate/glut.html

since im running on a 64bit machine i need to place the GLUT32.dll in the C:\Windows\SysWOW64 instead of C:\Windows\System32

i then place the GLUT.h in the:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include

and the GLUT32.lib in the
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib

in the visual studio dependencies i just add the following:
opengl32.lib
glut32.lib
glu32.lib








and im all set and good to go with the test program


/**********************************
  Simple.cpp
  A simple GLUT program.

****************************************************************************/

#include <string.h>
#include <glut.h>

void mydisplay( void )
{
    glClearColor (0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    /* set drawing/fill  color to white */

    glColor3f(1.0, 1.0, 1.0);

    /* set up standard orthogonal view with clipping */
    /* box as cube of side 2 centered at origin */
    /* This is default view and these statement could be removed */

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
    /* define unit square polygon */

    glBegin(GL_POLYGON);
    glVertex2f(-0.5, -0.5);
    glVertex2f(-0.5, 0.5);
    glVertex2f(0.5, 0.5);
    glVertex2f(0.5, -0.5);
    glEnd();

    /* flush GL buffers */

    glFlush();}


/**************************************** main() ********************/
void init()
{
    glClearColor (0.0, 0.0, 0.0, 1.0);

    glColor3f(1.0, 1.0, 1.0);

    glMatrixMode (GL_PROJECTION);   
    glLoadIdentity ();   
    glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); 
}

void main(int argc, char* argv[])
{
    glutInit(&argc,argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500,500);
    glutInitWindowPosition(0,0);
    glutCreateWindow("simple");
    glutDisplayFunc(mydisplay);

    init();

    glutMainLoop();

}

Sunday, September 4, 2011

Histogram computation on a video

Histograms are collected counts of data organized into a set of predefined bins


 refracted class for histogram: given an input image, output the histogram image


class atsHistogram
{
public:
    cv::Mat DrawHistogram(Mat src)
    {
        /// Separate the image in 3 places ( R, G and B )
         vector<Mat> rgb_planes;
         split( src, rgb_planes );

         /// Establish the number of bins
         int histSize = 255;

         /// Set the ranges ( for R,G,B) )
         float range[] = { 0, 255 } ;
         const float* histRange = { range };

         bool uniform = true; bool accumulate = false;

         Mat r_hist, g_hist, b_hist;

         /// Compute the histograms:
         calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
         calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
         calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );

         // Draw the histograms for R, G and B
         int hist_w = 400; int hist_h = 400;
         int bin_w = cvRound( (double) hist_w/histSize );

         Mat histImage( hist_w, hist_h, CV_8UC3, Scalar( 0,0,0) );

         /// Normalize the result to [ 0, histImage.rows ]
         normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
         normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
         normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );

         /// Draw for each channel
         for( int i = 1; i < histSize; i++ )
           {
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                              Scalar( 0, 0, 255), 2, 8, 0  );
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
                              Scalar( 0, 255, 0), 2, 8, 0  );
             line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
                              Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
                              Scalar( 255, 0, 0), 2, 8, 0  );
            }
         return histImage;
    }
private:
};