Tuesday, July 19, 2011

Artificial Intelligence (K Nearest Neighbor) in OPENCV

In pattern recognition, the k-nearest neighbor algorithm (k-NN) is a method for classifying objects based on closest training examples in the feature space. k-NN is a type of instance-based learning, or lazy learning where the function is only approximated locally and all computation is deferred until classification. The k-nearest neighbor algorithm is amongst the simplest of all machine learning algorithms: an object is classified by a majority vote of its neighbors, with the object being assigned to the class most common amongst its k nearest neighbors (k is a positive integer, typically small). If k = 1, then the object is simply assigned to the class of its nearest neighbor.

The k-NN algorithm can also be adapted for use in estimating continuous variables. One such implementation uses an inverse distance weighted average of the k-nearest multivariate neighbors. This algorithm functions as follows:
  1. Compute Euclidean or Mahalanobis distance from target plot to those that were sampled.
  2. Order samples taking for account calculated distances.
  3. Choose heuristically optimal k nearest neighbor based on RMSE done by cross validation technique.
  4. Calculate an inverse distance weighted average with the k-nearest multivariate neighbors.
Using a weighted k-NN also significantly improves the results: the class (or value, in regression problems) of each of the k nearest points is multiplied by a weight proportional to the inverse of the distance between that point and the point for which the class is to be predicted.


class atsKNN{
public :
    void knn(cv::Mat& trainingData, cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses, int K)
    {
        cv::KNearest knn(trainingData, trainingClasses, cv::Mat(), false, K);
        cv::Mat predicted(testClasses.rows, 1, CV_32F);
        for(int i = 0; i < testData.rows; i++) {
                const cv::Mat sample = testData.row(i);
                predicted.at<float>(i,0) = knn.find_nearest(sample, K);
        }

        float percentage = evaluate(predicted, testClasses) * 100;
        cout << "K Nearest Neighbor Evaluated Accuracy = " << percentage << "%" << endl;
        prediction = predicted;
    }
    void showplot(cv::Mat testData)
    {
        plot_binary(testData, prediction, "Predictions Backpropagation");
    }
private:
    cv::Mat prediction;

};



the simplicity of the algorithm in classifying things requires you to provide alot of training samples to ensure lots of vectors are created within K. therefore it is not optimized for speed and space. in addition to consuming alot of memory it is really slow.

Tuesday, July 12, 2011

Artificial Intelligence (support vector machine (SVM)) in OPENCV

A support vector machine (SVM) is a concept in computer science for a set of related supervised learning methods that analyze data and recognize patterns, used for classification and regression analysis. The standard SVM takes a set of input data and predicts, for each given input, which of two possible classes the input is a member of, which makes the SVM a non-probabilistic binary linear classifier. Given a set of training examples, each marked as belonging to one of two categories, an SVM training algorithm builds a model that assigns new examples into one category or the other. An SVM model is a representation of the examples as points in space, mapped so that the examples of the separate categories are divided by a clear gap that is as wide as possible. New examples are then mapped into that same space and predicted to belong to a category based on which side of the gap they fall on.



A Support Vector Machine (SVM) performs classification by constructing an N-dimensional hyperplane that optimally separates the data into two categories. SVM models are closely related to neural networks. In fact, a SVM model using a sigmoid kernel function is equivalent to a two-layer, perceptron neural network.  

 The accuracy of an SVM model is largely dependent on the selection of the model parameters.
 In the parlance of SVM literature, a predictor variable is called an attribute, and a transformed attribute that is used to define the hyperplane is called a feature. The task of choosing the most suitable representation is known as feature selection. A set of features that describes one case (i.e., a row of predictor values) is called a vector. So the goal of SVM modeling is to find the optimal hyperplane that separates clusters of vector in such a way that cases with one category of the target variable are on one side of the plane and cases with the other category are on the other size of the plane. The vectors near the hyperplane are the support vectors.

#include<opencv2/ml/ml.hpp>
class atsSVM
{
public:
    void svm(cv::Mat& trainingData, cv::Mat& trainingClasses, cv::Mat& testData, cv::Mat& testClasses) {


        //SVM Parameters
        CvSVMParams param = CvSVMParams();

        param.svm_type = CvSVM::C_SVC;
        param.kernel_type = CvSVM::RBF; //CvSVM::RBF, CvSVM::LINEAR ...
        param.degree = 0; // for poly
        param.gamma = 20; // for poly/rbf/sigmoid
        param.coef0 = 0; // for poly/sigmoid

        param.C = 7; // for CV_SVM_C_SVC, CV_SVM_EPS_SVR and CV_SVM_NU_SVR
        param.nu = 0.0; // for CV_SVM_NU_SVC, CV_SVM_ONE_CLASS, and CV_SVM_NU_SVR
        param.p = 0.0; // for CV_SVM_EPS_SVR

        param.class_weights = NULL; // for CV_SVM_C_SVC
        param.term_crit.type = CV_TERMCRIT_ITER +CV_TERMCRIT_EPS;
        param.term_crit.max_iter = 1000;
        param.term_crit.epsilon = 1e-6;

        // SVM training (use train auto for OpenCV>=2.0)
        CvSVM svm(trainingData, trainingClasses, cv::Mat(), cv::Mat(), param);

        cv::Mat predicted(testClasses.rows, 1, CV_32F);

        for(int i = 0; i < testData.rows; i++) {
            cv::Mat sample = testData.row(i);

            float x = sample.at<float>(0,0);
            float y = sample.at<float>(0,1);

            predicted.at<float>(i, 0) = svm.predict(sample);
        }
        float percentage = evaluate(predicted, testClasses) * 100;
        cout << "Support Vector Machine Evaluated Accuracy = " << percentage << "%" << endl;
    }
    void showplot(cv::Mat testData)
    {
        plot_binary(testData, prediction, "Predictions SVM");
    }
private:
        cv::Mat prediction;
};

 The major strengths of SVM are the training is relatively easy. No local optimal, unlike in neural networks. It scales relatively well to high dimensional data and the trade-off between classifier complexity and error can be controlled explicitly. The weakness includes the need for a good kernel function

Monday, July 11, 2011

Artificial Intelligence (Multilayered perceptrons) in OPENCV

Neural networks are models of biological neural structures. The starting point for most neural networks is a model neuron. Each input is modified by a weight, which multiplies with the input value. The neuron will combine these weighted inputs and, with reference to a threshold value and activation function, use these to determine its output. This behavior follows closely our understanding of how real neurons work.

 Neural neworks are typically organized in layers. Layers are made up of a number of interconnected 'nodes' which contain an 'activation function'. Patterns are presented to the network via the 'input layer', which communicates to one or more 'hidden layers' where the actual processing is done via a system of weighted 'connections'. The hidden layers then link to an 'output layer' where the answer is output as shown in the graphic below.

 Backpropagation is a common method of teaching artificial neural networks how to perform a given task.
 It is a supervised learning method, and is a generalization of the delta rule. It requires a teacher that knows, or can calculate, the desired output for any input in the training set. It is most useful for feed-forward networks (networks that have no feedback, or simply, that have no connections that loop). The term is an abbreviation for "backward propagation of errors". Backpropagation requires that the activation function used by the artificial neurons (or "nodes") be differentiable.

The Steps
1. Initilaize weights with random values
2. Present the input vector to the network
3. Evaluate the output of the network after a forward propagation of the signal
4.Calculate error (T - O) at the output units
5.Compute delta_wh for all weights from hidden layer to output layer ; backward pass
6.Compute delta_wi for all weights from input layer to hidden layer ; backward pass continued
7. Update weights 
8. Termination Criteria. Goto Step 2 for a xed number of iterations or an error.

#include<opencv2/ml/ml.hpp>
class atsANN
{

public:

    void atstraineval(cv::Mat trainingData, cv::Mat trainingClasses, cv::Mat testData, cv::Mat testClasses)
    {
        cv::Mat layers = cv::Mat(4, 1, CV_32SC1); //create 4 layers

        layers.row(0) = cv::Scalar(2); //input layer accepts x and y
        layers.row(1) = cv::Scalar(10);//hidden layer
        layers.row(2) = cv::Scalar(15);//hidden layer
        layers.row(3) = cv::Scalar(1); //output layer returns 1 or -1

        //Create the ANN
        CvANN_MLP ann;

        //ANN criteria for termination
        CvTermCriteria criter;
        criter.max_iter = 100;
        criter.epsilon = 0.00001f;
        criter.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;

        //ANN parameters
        CvANN_MLP_TrainParams params;
        params.train_method = CvANN_MLP_TrainParams::BACKPROP;
        params.bp_dw_scale = 0.05f;
        params.bp_moment_scale = 0.05f;
        params.term_crit = criter; //termination criteria

        ann.create(layers);
        // train
        ann.train(trainingData, trainingClasses, cv::Mat(), cv::Mat(), params);

        cv::Mat response(1, 1, CV_32FC1);
        cv::Mat predicted(testClasses.rows, 1, CV_32F);

        for(int i = 0; i < testData.rows; i++) {
            cv::Mat response(1, 1, CV_32FC1);
            cv::Mat sample = testData.row(i);

            ann.predict(sample, response);
            predicted.at<float>(i,0) = response.at<float>(0,0);

        }
        float percentage = evaluate(predicted, testClasses) * 100;
        cout << "Artificial Neural Network Evaluated Accuracy = " << percentage << "%" << endl;
        prediction = predicted;
      
    }
    void showplot(cv::Mat testData)
    {
        plot_binary(testData, prediction, "Predictions Backpropagation");
    }
private:
    cv::Mat prediction;
};

 There are many advantages and limitations to neural network analysis and to discuss this subject properly we would have to look at each individual type of network, which isn't necessary for this general discussion. In reference to backpropagational networks however, there are some specific issues potential users should be aware of.

  • Backpropagational neural networks (and many other types of networks) are in a sense the ultimate 'black boxes'. Apart from defining the general archetecture of a network and perhaps initially seeding it with a random numbers, the user has no other role than to feed it input and watch it train and await the output. In fact, it has been said that with backpropagation, "you almost don't know what you're doing". Some software freely available software packages (NevProp, bp, Mactivation) do allow the user to sample the networks 'progress' at regular time intervals, but the learning itself progresses on its own. The final product of this activity is a trained network that provides no equations or coefficients defining a relationship (as in regression) beyond it's own internal mathematics. The network 'IS' the final equation of the relationship.
  • Backpropagational networks also tend to be slower to train than other types of networks and sometimes require thousands of epochs. If run on a truly parallel computer system this issue is not really a problem, but if the BPNN is being simulated on a standard serial machine (i.e. a single SPARC, Mac or PC) training can take some time. This is because the machines CPU must compute the function of each node and connection separately, which can be problematic in very large networks with a large amount of data. However, the speed of most current machines is such that this is typically not much of an issue. 
 #include <iostream>
#include <math.h>
#include <string>

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

//required for multilayer perceptrons
    #include<opencv2/ml/ml.hpp>


using namespace cv;
using namespace std;

#define numTrainingPoints 200
#define numTestPoints 2000
#define size 200

// accuracy
float evaluate(cv::Mat& predicted, cv::Mat& actual) {
    assert(predicted.rows == actual.rows);
    int t = 0;
    int f = 0;
    for(int i = 0; i < actual.rows; i++) {
        float p = predicted.at<float>(i,0);
        float a = actual.at<float>(i,0);
        if((p >= 0.0 && a >= 0.0) || (p <= 0.0 &&  a <= 0.0)) {
            t++;
        } else {
            f++;
        }
    }
    return (t * 1.0) / (t + f);
}

// plot data and class
void plot_binary(cv::Mat& data, cv::Mat& classes, string name) {
    cv::Mat plot(size, size, CV_8UC3);
    plot.setTo(CV_RGB(255,255,255));
    for(int i = 0; i < data.rows; i++) {

        float x = data.at<float>(i,0) * size;
        float y = data.at<float>(i,1) * size;

        if(classes.at<float>(i, 0) > 0) {
            cv::circle(plot, Point(x,y), 2, CV_RGB(255,0,0),1);
        } else {
            cv::circle(plot, Point(x,y), 2, CV_RGB(0,255,0),1);
        }
    }
    cv::imshow(name, plot);
}

// function to learn
int dataLearn(float x, float y) {
    return y > atan(y/x) ? -1 : 1;

}

// label data with equation
cv::Mat labelData(cv::Mat points) {
    cv::Mat labels(points.rows, 1, CV_32FC1);
    for(int i = 0; i < points.rows; i++) {
             float x = points.at<float>(i,0);
             float y = points.at<float>(i,1);
             labels.at<float>(i, 0) = dataLearn(x, y);
        }
    return labels;
}

class atsANN
{

public:

    void atstraineval(cv::Mat trainingData, cv::Mat trainingClasses, cv::Mat testData, cv::Mat testClasses)
    {
        cv::Mat layers = cv::Mat(4, 1, CV_32SC1); //create 4 layers

        layers.row(0) = cv::Scalar(2); //input layer accepts x and y
        layers.row(1) = cv::Scalar(10);//hidden layer
        layers.row(2) = cv::Scalar(15);//hidden layer
        layers.row(3) = cv::Scalar(1); //output layer returns 1 or -1

        //Create the ANN
        CvANN_MLP ann;

        //ANN criteria for termination
        CvTermCriteria criter;
        criter.max_iter = 100;
        criter.epsilon = 0.00001f;
        criter.type = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;

        //ANN parameters
        CvANN_MLP_TrainParams params;
        params.train_method = CvANN_MLP_TrainParams::BACKPROP;
        params.bp_dw_scale = 0.05f;
        params.bp_moment_scale = 0.05f;
        params.term_crit = criter; //termination criteria

        ann.create(layers);
        // train
        ann.train(trainingData, trainingClasses, cv::Mat(), cv::Mat(), params);

        cv::Mat response(1, 1, CV_32FC1);
        cv::Mat predicted(testClasses.rows, 1, CV_32F);

        for(int i = 0; i < testData.rows; i++) {
            cv::Mat response(1, 1, CV_32FC1);
            cv::Mat sample = testData.row(i);

            ann.predict(sample, response);
            predicted.at<float>(i,0) = response.at<float>(0,0);

        }
        float percentage = evaluate(predicted, testClasses) * 100;
        cout << "Artificial Neural Network Evaluated Accuracy = " << percentage << "%" << endl;
        prediction = predicted;
      
    }
    void showplot(cv::Mat testData)
    {
        plot_binary(testData, prediction, "Predictions Backpropagation");
    }
private:
    cv::Mat prediction;
};

int main() {

    cv::Mat trainingData(numTrainingPoints, 2, CV_32FC1);
    cv::Mat testData(numTestPoints, 2, CV_32FC1);

    cv::randu(trainingData,0,1); //generate uniformly-distributed random numbers from the range [low, high)
    cv::randu(testData,0,1);

    cv::Mat trainingClasses = labelData(trainingData);
    cv::Mat testClasses = labelData(testData);

    plot_binary(trainingData, trainingClasses, "Plot of Training Data");
    plot_binary(testData, testClasses, "Plot ofTest Data");

    atsANN myANN;
    myANN.atstraineval(trainingData,trainingClasses,testData,testClasses);
    myANN.showplot(testData);


    cv::waitKey();

    return 0;
}

Wednesday, July 6, 2011

OPENCV2 C++ basics (OOP) old style vs new style

Version 2.2 of the opencv library has divided its library into modules.
These library modules have their own associated header files which is required therefore any code you see on the internet that looks like this:
#include "cv.h"
#include <cv.h> and so on
is done using the old style, C or C++. However after the restructuring of the opencv library into modules, not all functionality was ported to the new style:
#include <opencv2/core/core.hpp>

one example of such method is the LOGPOLAR transform.
Another thing to take note here is, the old deprecated IplImage is changed to the matrix cv::Mat. therefore you should avoid using such unless your using the old style.

For the Log-Polar Transform i will be using this header file:
#include <opencv2/imgproc/imgproc_c.h>

Also to make things easy, ive created a class to convert between cv::Mat and IplImage
class atsoldtonew
{
public:
    IplImage convertOld(cv::Mat MatImage)
    {
        IplImage img =MatImage;
        return img;
    }
    cv::Mat convertNew(IplImage *iplImage)
    {
        cv::Mat imgMat(iplImage);
        return imgMat;
    }
private:
};

So the Log-Polar Class is the following:
class atslogPolar
{
public:
    void convert(cv::Mat image)
    {
        IplImage src;
        IplImage img =image;
        IplImage* newsrc = &img;
        IplImage* dst = cvCreateImage( cvGetSize(newsrc), 8, 3  );
        IplImage* dst2 = cvCreateImage( cvGetSize(newsrc), 8, 3  );

        cvLogPolar( newsrc, dst, cvPoint2D32f(image.size().width/2,image.size().height/2), 40,
        CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS );
               
        cvLogPolar( dst, dst2, cvPoint2D32f(image.size().width/2,image.size().height/2), 40,
        CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS+CV_WARP_INVERSE_MAP );

        newIm = dst;
        newImage = dst2;
    }
    void view()
    {
        cvShowImage( "inverse log-polar", newIm );
        cvShowImage( "reinverse log-polar", newImage );
    }
    IplImage* logpolar()
    {
        return newIm; //return the local variable
    }
    IplImage* Inverselogpolar()
    {
        return newImage; //return the local variable
    }
private:
    IplImage* newIm; //keep a local variable
    IplImage* newImage; //keep a local variable
};

This Class uses the old style however by converting to and from cv::Mat to IplImage your main function can be written as the following

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main()
{
    atscameraCapture mine; //initialize
    for(;;)
        {
        cv::Mat image = mine.ats_getImage();
        cv::imshow("image",image); //CAMERA IMAGE
       
        atslogPolar pl;
        pl.convert(image);
        pl.view();   //show using the old style
        atsoldtonew conv;
        cv::imshow("image logpolar",conv.convertNew(pl.logpolar()));  //show using the new style

        if(cv::waitKey(30) >= 0) break;
        }
    return 0;
}
You can easily add the class to my previous Post code to add the new added functionality

Tuesday, July 5, 2011

OPENCV2 C++ basics (OOP) edge detection using CANNY

if you have an OK background on C++ you can follow easily:

ive seperated the camera capturing and the edge detection to separate classes.
the main entry point remains the same.
you can use the code to add a new class to process images using the atscameraCapture class.

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

//for atsEdges
#include <opencv2/imgproc/imgproc.hpp>

class atscameraCapture
{
public:
    cv::Mat ats_getImage()
    {
        //check if Camera is still open
        if (!cap.isOpened())
        {
            std::cout< "Cant Capture";
            exit(0);
        }
        //camera is open so get a frame
            cv::Mat frame;
            cap>> frame;
        //return the frame
            return frame;
    }
    atscameraCapture()
    {
        //create a camera capture and assign it to a local private variable
        cv::VideoCapture capture(0);
        cap = capture;
    }
private:
    cv::VideoCapture cap;
};


class atsEdges
{
public:
    cv::Mat getEdges(cv::Mat image)
    {
        cv::Mat edges;
        cvtColor(image,edges,CV_BGR2GRAY); //convert from RGB color space to GRAY
        Canny(edges,edges,
            0,    // double threshold1
            30,    //double threshold2
            3); //int apertureSize=3, bool L2gradient=false)
        return edges;

    }
   
private:
};

int main()
{
    atscameraCapture mine; //initialize
    for(;;)
        {
        cv::Mat image = mine.ats_getImage();
        cv::imshow("image",image);
       
        atsEdges pl;

        cv::imshow("pl image",pl.getEdges(image));

        if(cv::waitKey(30) >= 0) break;
        }
    return 0;
}

Sunday, July 3, 2011

build a cascade of boosted classifiers based on Haar-like features

Step 1 -


Tools Needed:
You can find these executables as part of the opencv default installation. they are located in the BIN folder

Samples:
you should have alot of positive and negative images.
Take note that compressed images such as jpeg have certain issues therefore selecting a BMP image is recommended.

Create folders for the Negative and Positive images and include an info file aswell

Basically there should be four sets of images that you work on:
- a positive sample set representing your object that you want to train
- another positive sample set for testing purpose
- a negative sample set (or so-called backgrounds) for training
- and another negative sample set for testing, too

so your 2 folders containing the images are:
/opencv/project/negative
/opencv/project/positive
as well as the info file:
-train.txt
-test.txt

To work with these images in the utilities, a list of the files is needed. Using Win32 as host OS,
you can get the list via command promt. Move to the folder where the info file will be stored and
type “dir /b /s > infofile.txt”. Open “infofile.txt” and make the paths relative to the
info file (i.e. find “C:\Temp\negatives\” and replace with “”).

Step 2 - Test Creation
Once your done preparing you need to create a vec-file in the data folder. This is done by createsamples.exe by calling:
opencv_createsamples.exe -info positives/train.txt -vec data/positives.vec -num 1300 -w 20 –h 20
the numbers show 1300 samples with a width and height of 20
double check if the vec file really contains the desired images.

Step 3 Training
Im assuming the following the default values of hitrate (0.995), maxfalsealarm (0.5), weighttrimming (0.95) and boosting type (GAB, “Gentle Ada Boost”) are good in most cases we use the following call:"

haartraining.exe -data data/cascade -vec data/positives.vec -bg negatives/train.txt
-npos 1300 -nneg 5000 -nstages 30 -mem 1300 -mode ALL -w 20 -h 20
- 1300 positive images
- 5000 negative images
- 30 stages
- 1300 MB of RAM

Step 4 Testing

the test of hitrate and falsealarm will be done by calling

performance.exe -data data/cascade -info positives/testing/testing.txt -w 20 -h 20 -rs 30

Thats it, tell me how it goes.,,,