Sunday, July 1, 2012

Binary heap based priority queue in Qt

Long time ago, I posted implementation of Priority Queue implemented using Qt's
 QQueue data structure. Here is old post.

That code was offering o(n) performance for enqueue operation and o(1) performance for dequeue operation. This might be acceptable for small data set. But for large data set you might want to use Priority queue based on Binary heap implementation.

I tried to implement my old priority queue using Binary heap, here is my implementation.

Following code implements BinaryHeap using QList. BinaryHeap class implements enqueue, dequeue and count method.
template <class T>
class BinaryHeap {
public:

    void enqueue(T item) {
        mList.append(item);
        int i = mList.count() - 1;
        int parent = (i-1)/2;
        while( parent >= 0 && mList[i] < mList[parent] ) {
            T temp = mList[parent];
            mList[parent] = mList[i];
            mList[i] = temp;
            i = parent;
            parent = (i-1)/2;
        }
    }

    T dequeue() {
        if( mList.isEmpty()) {
            return T();
        }

        T item = mList[0];
        int i = 0;
        mList[0] = mList[ count()-1];
        mList.removeLast();
        while( i < count() ) {
            int left = 2*i+1;
            int right = left + 1;

            if( right > count() - 1) {
                break;
            }

            int min = left;
            if( mList[right] < mList[left] ) {
                min = right;
            }

            if( mList[i] > mList[min] ) {
                T data = mList[min];
                mList[min] = mList[i];
                mList[i] = data;
                i = min;
            } else {
                break;
            }
        }
        return item;
    }

    int count() const {
        return mList.count();
    }

private:
    QList<T> mList;
};

And based on above BinaryHeap class, following is my PriorityQueue class.
enum Priority {
    Low = 2,
    Normal = 1,
    High = 0
};

template <class T>
class PriorityQueue
{
public:
    void enqueue( Priority priority, T data) {
        Item item(priority,data);
        mHeap.enqueue(item);
    }

    T dequeue() {
        Item item =  mHeap.dequeue();
        return item.mData;
    }

    int count() const {
        return mHeap.count();
    }

private:

    BinaryHeap<Item> mHeap;
};
And Item class looks like below.
    class Item{
    public:
        Item() {
        }

        Item(Priority priority, T data ):
            mPriority(priority),mData(data)
        {}

        bool operator<(const Item& other) {
            return mPriority < other.mPriority;
        }

        Priority mPriority;
        T mData;
    };


Monday, May 28, 2012

Tracking color in image and detecting gesture in Qt

In this blog post I have written how we can access the individual frame from QCamera, In this blog post I will show how to use those frame to track some particular colored object and detecting gesture from motion of that object.

Following is demo of my sample application running on N9.


Tracking colored object

I don't know the actual algorithm for detecting color in image but i created simple algorithm that will detects some predefined color in image. Please note that if image has multiple object with the color which we are tracking it will return rectangle which cover all objects, not individual rectangle of each object.

As I am not interested in details of captured image,  just interested in checking if image has defined color object or not, I reduces size of image to half, so i have to process small number of pixel to detect color.

Then to detect color in image, I convert image capture from camera from RGB color spec to HSV color spec, as its quite easy to process HSV color spec to detect color.

After image is converted to HSV color spec, I converted image to black and white image, black portion will be detected object and rest of things will be in white. After getting this image I just need to scan image to find area of black portion of image.

So now I have coordinate of colored object which we are detecting.

Following code implements the above logic to detect the red colored object, in code I combined process of converting image to black and white and detect the black portion of image.

QRect ColorMotionDetector::detectColor( const QImage& origImage)
{
    //reduce size of image
    QImage image(origImage);
    image = image.scaled(QSize(320,240));

    emit originalImage(image);

    //rectanlge of detected colored object
    int maxX = -1;
    int minX = 99999;
    int maxY = -1;
    int minY =  99999;

    int width = image.width();
    int height = image.height();
    bool detected = false;

    //black and white image
    QImage converted(image.size(),image.format());

    for (int y = 0; y< height; ++y ) {
        for( int x = 0; x < width; ++x ) {
            //convert individual pixel to HSV from RGB
            QRgb pixel = image.pixel(x,y);
            QColor color(pixel);
            color = color.toHsv();

            
            //default whitel color for other object
            QRgb newPixel = qRgb(255, 255, 255);
            
            //detecting red color
            if( color.hue() >= 0 && color.hue() <= 22
                    && color.saturation() <= 255 && color.saturation() >= 240
                    && color.value() <= 255 && color.value() >= 100 ) {

                detected = true;

                if( x > maxX ) {
                    maxX = x;
                } else if( x < minX )  {
                    minX = x;
                }

                if( y > maxY ) {
                    maxY = y;
                } else if( x < minY )  {
                    minY = y;
                }
                
                //black color for detected object
                newPixel = qRgb(0, 0, 0);
            } 
            converted.setPixel(x,y,newPixel);
        }
    }

    QRect rect;
    if( detected) {
        rect = QRect(minX,minY, maxX - minX, maxY-minY );

        //drawing red rectangle around detected object
        QPainter painter( &converted );
        painter.setPen(QPen(Qt::red));
        painter.drawRect(rect);
        painter.end();
    }
    emit processedImage(converted);

    return rect;
}
Detecting swipe gesture

When we detect the position of object using above color detection code, we can use that position to detect if position tracked from individual image create some kind of gesture.

I will show how to use captured position to detect horizontal swipe gesture, we can easily extend it to detect vertical swipe or diagonal swipe.

I used following logic to detect swipe gesture,

> As color detection code returns position of tracked object, We compare this new position with its old position.
> If there is any progress in motion of object, we add difference of x coordinate to total progress made. In case of no progress, we discard whole gesture and reset variable that keep track of motion.
> While doing so if we detect certain amount of movement in particular direction, we decide if gesture was left swipe or right swipe using difference in position of object and reset the variables.
Following code implement above logic.

Gesture ColorMotionDetector::detectGesture(QRect rect) {

    //not valid rectangle, mean no object detected
    if( !rect.isValid()) {
        mLastRect = QRect();
        mXDist = 0;
        return Invalid;
    }

    //there is no previous cordinate, store rect
    if( !mLastRect.isValid() ) {
        mLastRect = rect;
        mXDist= 0;
        return Invalid;
    }

    Gesture gesture = Invalid;
    int x = rect.x();
    int lastX = mLastRect.x();
    int diff = lastX - x;

    mLastRect = rect;
    //check if there is certain amount of movement
    if( qAbs( diff ) > 10 ) {
        //there is movement in x direction, store amout of movement in total movement
        mXDist += diff;
       
        //x motion match to amount required for perticular gesture
        //check if motion of let to right or right to left
        if( mXDist >  150 ) {
            qDebug() << "Right horizontal swipe detected..." << mXDist;
            mXDist = 0;
            gesture = SwipeRight;
        } else if ( mXDist < -150 ) {
            qDebug() << "Left horizontal swipe detected..." << mXDist;
            mXDist = 0;
            gesture = SwipeLeft;
        }
    } else {
        //discard the gesture
        mXDist = 0;
        mLastRect = QRect();
    }
    return gesture;
}


Putting all together

Now we have code that detect colored object and code that detect gesture. Following code shows how those function are used together.

//detection motion from captured image from camera
void ColorMotionDetector::detectMotion( const QImage& image) {

    QRect rect = detectColor( image);
    Gesture gesture = detectGesture( rect );

    if( gesture != Invalid ) {
        emit gestureDetected( gesture );
    }
}

Following is vary simple gesture handler, which just print handled gesture.

void MyWidget::gestureDetected( Gesture gesture) {

    if( gesture ==  SwipeLeft) {
        mSwipeLabel->setText("Left swipe");
    } else if( gesture == SwipeRight) {
        mSwipeLabel->setText("Right swipe");
    }
}

Wednesday, May 2, 2012

Capturing Hot Key on Windows with Qt

Generally I don't use windows, but at work I need to use windows sometime and on windows I required to create one utility application, which can be activated by hotkey like, Alt + Tab.

There is RegisterHotKey windows API, by calling it we can register special key combination as Hot Key. By winEvent event handler, we can respond to Hot Key event.

Following is my code, that register CTRL+SHIT+SPACE as Hot key and responds it in winEvent handler.
DefineWordWidget::DefineWordWidget(QWidget *parent) :
    QWidget(parent)
{
    RegisterHotKey(winId(), 100, MOD_CONTROL|MOD_SHIFT, VK_SPACE);
    mClipBoard = QApplication::clipboard();
}

bool DefineWordWidget::winEvent(MSG *message, long *result)
{   
    if( message->message == WM_HOTKEY) {
        QString originalText = mClipBoard->text();
        qDebug() << "ClipBoard:" << originalText;
        if( !originalText.isEmpty()) {
            //show widget if minimized
            this->setWindowState((this->windowState() & ~Qt::WindowMinimized)
                | Qt::WindowActive);
        }
        return true;
    }
    return false;
}

Friday, April 27, 2012

Upload photo on facebook using Qt

Some time back I wrote blog post about how to Post message on facebook wall, Here is link.

This post I will show how to upload a photo from local machine to facebook. On facebook photo can be uploaded to application's album or existing album created by user or application.

Where photo will be uploaded is decided by URL used during http requst.

https://graph.facebook.com/USER_ID/photos will upload photo to application's album, https://graph.facebook.com/ALBUM_ID/photos will upload photo to specific album indicated by ALBUM_ID.

My code describe method to upload photo to application's album. You can visit this post to know how to login and how to post message on facebook wall.

This post describe process how to upload photo using php, I tried to convert that code to Qt code. As code shows that photo is uploaded using multipart/form-data method, we need to create post request with multipart/form-data method.

Following code describe the process.

void FacebookHelper::uploadPicture(const QString& picLocation, 
    const QString& comment) {
    if( !isAuthorized() ) {
        qDebug() << "Please login first...";
        emit messageStatus(1,"Please login first...");
        return;
    }

   // Show photo upload form to user and post to the Graph URL
    QString uploadUrl = "https://graph.facebook.com/me/photos?access_token="
       + mAccessToken;

    QFileInfo fileInfo(picLocation);
    QFile file(picLocation);
    if (!file.open(QIODevice::ReadWrite)) {
        qDebug() << "Can not open file:" << picLocation;
        emit messageStatus(2,"Could not open file" + picLocation);
        return;
    }

    QString bound="---------------------------723690991551375881941828858";
    QByteArray data(QString("--"+bound+"\r\n").toAscii());
    data += "Content-Disposition: form-data; name=\"action\"\r\n\r\n";
    data += "\r\n";
    data += QString("--" + bound + "\r\n").toAscii();
    data += "Content-Disposition: form-data; name=\"source\"; filename=\""
             +file.fileName()+"\"\r\n";
    data += "Content-Type: image/"+fileInfo.suffix().toLower()+"\r\n\r\n";
    data += file.readAll();
    data += "\r\n";
    data += QString("--" + bound + "\r\n").toAscii();
    data += QString("--" + bound + "\r\n").toAscii();
    data += "Content-Disposition: form-data; name=\"message\"\r\n\r\n";
    data += comment.toAscii();
    data += "\r\n";
    data += "\r\n";

    QNetworkRequest request(uploadUrl);
    request.setRawHeader(QByteArray("Content-Type"),
           QString("multipart/form-data; boundary=" + bound).toAscii());
    request.setRawHeader(QByteArray("Content-Length"),
          QString::number(data.length()).toAscii());
    mCurrentRequest = mNetManager.post(request,data);
    connect(mCurrentRequest,SIGNAL(finished()),this,SLOT(messageResponse()));
}

Monday, April 9, 2012

Logitech MK220 Wireless Combo

Yesterday I received my logitech wireless mouse and keyboard(MK220 Wireless Combo). I wanted to use it with my ubuntu laptop and was worried if it will be compatible with my ubuntu or not.

Before purchasing I tried to check logitech support site to verify it works with linux or not, but logitech site list support for only windows. But I still purchased it and hoping of it to work fine. But after receiving it, I plugged its wireless signal receiver to my laptop and inserted battery to keyboard and mouse and It worked, without my doing anything. Well I was thinking, I will be required do some hack to make it work, but I am glad that ubuntu has out of box support for this device.

After receiving it I tried to use keyboard, keyboard is lightweight and vary portable, initially I felt that its keys are cramped and my finger can't find keys that easily as it do on full size keyboard and also found its arrow Key are placed at awkward position and difficult to find it easily but after working a while on it now it quite comfortable.

Following are few snaps of product. I shared this post here in hope to remove similar doubts of compatibility that I had before purchasing this product.