Sunday 26 February 2012

Copying and Tiling with OpenCV

Recently,a friend of mine asked, how to perform the matlab function repmat(A,m,n) in opencv.
Okay, for those of you, who want to know what repmat does, this is a normal image


a.k.a Normal Image

Now, after you tile it, this is what it looks like
a.k.a tiled image
This is the basic functionality of repmat

Now, If you are new to OpenCV, this might seem a little hard at first. But I'll explain with the code first and if you still have any doubts, send me a mail.

The basic operation is simple, copy the source image into a specific region of the destination image. Repeat this n times. Here, two questions might arise, How do you copy the original image and how the hell do you choose the region. The answer to your questions can all be found in the explanation with code section below :-




// A program for copying and tiling an image in opencv
// Author - Sidhartha Mani , sidharthamani@me.com
// editor - vIMproved
// OS - Mac OSX 10.6 Snow Leopard
// cflags '-I/usr/local/include -L/usr/local/lib' 
// libs '-lopencv_core -lopencv_imgproc -lopencv_highgui'


// The modules used here are the cv module, or the core module, which lets you use basic image processing tools and other memory management techniques
// highgui - for saving images and loading them from file system and also for displaying it using GUI
// iostream for CLI I/O

#include <iostream>
#include <opencv/cv.h>
#include <opencv/highgui.h>
using namespace std;

int main(int argc, char **argv)
{
//  A placeholder for number of tiles
    int m,n;

    if(argc < 5)
    {
// Check input arguments and ensure they are all right
        cout << "Usage : ./tile src-image m n dest-img\n m and n values <= 9  and  >= 1 \n";
        return 0;
    }

//create a new image pointer to point to the source image
    IplImage *img = cvLoadImage(argv[1],1);
    

//take care of non existent source images
    if(!img)
    {
        printf(" image could not be loaded, check if file exists \n");
        return 0;
    }
    try
    {
        m = atoi(argv[2]);
        n = atoi(argv[3]);
    }
    catch(...)
    {
        cout<< "m and n values are invalid";
    }
    cvNamedWindow("Window");

    //let us say, u want to tile this m times horizontally, and n times vertically
    // calculate size of new image

    int newWidth = img->width * m;
    int newHeight = img->height *n;

// Create a new image to store the tiled image
// 2nd parameter is the pixel size and 3rd parmeter is the number of
// channels(R,G,B)

    IplImage *newImg = cvCreateImage(cvSize(newWidth, newHeight), IPL_DEPTH_8U, 3);
    

// loop counters
    int left_top_x = 0;
    int left_top_y = 0;
    
    //loop from starting position to end
    for(;left_top_y < newHeight;left_top_x += (img->width))
    {
        if(left_top_x >= newWidth)
        {
            left_top_x = -img->width;
            left_top_y += img->height;
            continue;
        }
// set the desired region of interest        
        cvSetImageROI(newImg, cvRect(left_top_x,left_top_y, img->width,img->height));
// copy to that region
        cvCopy(img, newImg);
// reset region of interest
        cvResetImageROI(newImg);
    }
// show images of happy faces :)
    
    cvShowImage("Window", newImg);
    try
    {
    cvSaveImage(argv[4], newImg);
    }
    catch(...)
    {
        cout<<" destination file name invalid\n";
    }
    cvWaitKey(0);
    return 0;
}





Getting back to the code, I first created a pointer to the source image with :-
    IplImage *img = cvLoadImage(argv[1],1);
This command, not only creates a pointer to the source image, but also allocates memory for it and initializes it. 

Now, that the source image is loaded, we need to create space for the destination image. This is done by the statement :-
    IplImage *newImg = cvCreateImage(cvSize(newWidth, newHeight), IPL_DEPTH_8U, 3);

Now that both images are ready, I need to set the region of interest to this region first :- 

Then , this Region :-

and so on...

This is done by the first and second iterations of this loop :-
    for(;left_top_y < newHeight;left_top_x += (img->width))
    {
        if(left_top_x >= newWidth)
        {
            left_top_x = -img->width;
            left_top_y += img->height;
            continue;
        }
// set the desired region of interest        
        cvSetImageROI(newImg, cvRect(left_top_x,left_top_y, img->width,img->height));
// copy to that region
        cvCopy(img, newImg);
// reset region of interest
        cvResetImageROI(newImg);
    }


Any C guy(or gal) will be able to understand what this loop does, but anyhoo, I'll tell it to you, It starts from the initial position (0,0) - which is the left top corner( the co-ordinate system starts from left top corner and increases along the 4th quadrant of cartesian co-ordinates). Then it sets the working region to the source image dimensions by using a binary mask. This is done by the function cvSetImageROI(IplImage *Img, CvRect *Rect). This function takes the image and sets the Region of interest(ROI) to the rectangle represented by Rect).The image can now be copied from source to destination by using cvCopyImage(IplImage *Src, IplImage *Dest). Then It updates the regions of interest to the new location by adding the image width to it. When we reach the end of destination image size, then we reset the x co-ordinate to 0 and add the y  co-ordinate to the precious starting position. Repeat this n times and you have your results:-



Voila!

The code for the above program is available with much less comments at http://db.tt/usbh3nLB

2 comments:

  1. Thanks a lot, however ¿isn't possible to avoid the copy?

    About the code:
    ¿Since when does atoi() throw an exception?
    And if you are going to catch it inmediately, then just use error codes.

    ReplyDelete
  2. hi, thank you for your comments, it is possible to avoid the copy, but it wont give any huge processing gains as the number of memory reads and writes will almost be the same.

    About the code :
    atoi() throws an arithmetic exception if the input is not a number! we could use assertions instead of exception handling!

    ReplyDelete