Wednesday 30 January 2013

Getting started with QWT

I have been looking for a graphing API for QT and eventually found one that suits my needs called QWT ( Qt Widgets for Technical Applications). I ran into some issues while setting it up but finally got it running so here is the resulting tutorial on how to set-up QWT in QT creator (for Linux 12.0.4).
Now we can begin, first step is to download the source files from here. The version used for this tutorial is QWT 6.1. In the terminal, navigate to the location of the downloaded .tar.bz2 file, then type the following commands:
$ tar -xjvf qwt-6.1-rc3.tar.bz2
$ cd qwt-6.1-rc3
$ qmake qwt.pro
$ make
$ make install
Launch QT creator and then Go to File > New File or Project. Select a QT Gui Application give the project a name e.g. “FirstQwtProject”.You could leave the other settings in the wizard as they are or change them to suit your own projects. I have kept the 'FirstQwtProject.pro' ( this will vary depending on the Project Name you have chosen) and 'main.cpp' but deleted the remaining files (MainWindow class) as they would not be need for this simple introduction. To run QWT programs in QT creator we need to let the IDE know where to find the QWT libraries.
So open the .pro file associated with the project and append the following lines at the end of the file. This should be included in every QWT Project you create but remember to change the include Path "/usr/local/qwt-6.1.0-rc3/.." to the location of the QWT install directory on your PC.
CONFIG += qwt
INCLUDEPATH +="/usr/local/qwt-6.1.0-rc3/include"
LIBS += -L/usr/local/qwt-6.1.0-rc3/lib -lqwt
Now go to the 'main.cpp' file and type in the following lines of code and press Ctrl+R to run it.
#include <QApplication>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_symbol.h>
#include <qwt_legend.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QwtPlot plot;
    plot.setTitle( "Plot Demo" );
    plot.setCanvasBackground( Qt::white );
    plot.setAxisScale( QwtPlot::yLeft, 0.0, 10.0);
    plot.insertLegend( new QwtLegend() );

    QwtPlotGrid *grid = new QwtPlotGrid();
    grid->attach( &plot );

    QwtPlotCurve *curve = new QwtPlotCurve();
    curve->setTitle( "Pixel Count" );
    curve->setPen( Qt::blue, 4 ),
    curve->setRenderHint( QwtPlotItem::RenderAntialiased, true );

    QwtSymbol *symbol = new QwtSymbol( QwtSymbol::Ellipse,
        QBrush( Qt::yellow ), QPen( Qt::red, 2 ), QSize( 8, 8 ) );
    curve->setSymbol( symbol );

    QPolygonF points;
    points << QPointF( 0.0, 4.4 ) << QPointF( 1.0, 3.0 )
        << QPointF( 2.0, 4.5 ) << QPointF( 3.0, 6.8 )
        << QPointF( 4.0, 7.9 ) << QPointF( 5.0, 7.1 );
    curve->setSamples( points );

    curve->attach( &plot );

    plot.resize( 600, 400 );
    plot.show();

    return a.exec();
}
This is what you should see.

Conclusion

QWT Libraries are one of many options for plotting graphs in QT. In the next tutorial, I will show you how to plot histograms calculated via OpenCV with QWT.

Monday 28 January 2013

Find Holes in a binary image

Since OpenCV is yet to provide special functions for blob analysis. I will show you a simple method of determining the number of holes in an image. This method exploits the ability of OpenCV's findContours() function to extract and distinguish between outer contour (shape boundaries) and inner contours (hole boundaries). For this tutorial, I assume the input image is a binary image. We will be using the 2 images below.


Figure 1: A) a complex shape with multiple levels of contours. B) a simple shape with 2 holes 

The result from the application are the following images

Figure 2:  Resulting images showing location of holes in A)  Figure 1A B) Figure 1B.

Code

Here is the full code:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
   if( argc != 2)
     {
       cout << " No image file is specified \n ";
       return -1;
     }

    Mat src = imread(argv[1]);

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

    findContours( src.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE );

    Mat singleLevelHoles = Mat::zeros(src.size(), src.type());
    Mat multipleLevelHoles = Mat::zeros(src.size(), src.type());


    for(vector<Vec4i>::size_type idx=0; idx<hierarchy.size(); ++idx)
    {
        if(hierarchy[idx][3] != -1)
           drawContours(singleLevelHoles, contours, idx, Scalar::all(255), CV_FILLED, 8, hierarchy);
    }

    bitwise_not(src, src);
    bitwise_and(src, singleLevelHoles, multipleLevelHoles);

    //Inverse source image.
    imwrite("/home/stephen/Pictures/Result0.jpg", src);

    //Holes before the bitwise AND operation.
    imwrite("/home/stephen/Pictures/Result1.jpg", singleLevelHoles);

    //Holes after the bitwise AND Operation.
    imwrite("/home/stephen/Pictures/Result2.jpg", multipleLevelHoles);

    return 0;
}

Explanation

  1. Lines 1-4: Standard includes from the OpenCV and C++ libraries.
  2. Lines 11-17: Error checking command line input. The image file path is passed in as command line input; the image is read into memory . 
  3. Lines 19-22: We extract contours along with the hierarchy information about the contours using the findContours() function in OpenCV. The contour retrieval mode is set to CV_RETR_TREE which simply retrieves the contour and reconstructs a full-hierarchy of nested contours. In other words, this mode causes the function to return a vector containing an entry for each contour; each entry is an array of 4 values. We are concerned with the last value of this array which tells us the parent of the contour. This value is set to -1 for outer contours otherwise it is set to the index of the parent contour in the hierarchy vector. For more information on contour hierarchy see here. The contour approximation method is  set to CV_CHAIN_APPROX_NONE which basically tells the function to store all contour points.
  4. Lines 28-32:  After extracting the contours, we use the hierarchy information to draw all the holes using the drawContours() function;  the thickness is set to CV_FILLED so that the drawn contours are filled with white pixels (255). This is perfect for when you do not have multiple levels of contours like Figure 1B but for the figure 1A the result would be as shown below.
    Figure 3: Resulting image showing holes in Figure 1A before applying logical operation.

  5. Lines 34-35: The result above could be undesirable for some operations; so the bitwise NOT and AND logical operations are used to get the location the inner contours of the image.

Conclusion

This is just a simple way to extract holes from an image without having to bother about extra libraries like cvBLobLib. Hope it was helpful. Happy Coding!